瀏覽代碼

show full history on pages and files

Damien ACCORSI 9 年之前
父節點
當前提交
1a0ea4b571

+ 0 - 1
tracim/tracim/controllers/workspace.py 查看文件

79
 
79
 
80
         if not current_id:
80
         if not current_id:
81
             # Default case is to return list of workspaces
81
             # Default case is to return list of workspaces
82
-            print('ignore : ', ignored_ids)
83
             api = WorkspaceApi(tmpl_context.current_user)
82
             api = WorkspaceApi(tmpl_context.current_user)
84
             workspaces = api.get_all_for_user(tmpl_context.current_user,
83
             workspaces = api.get_all_for_user(tmpl_context.current_user,
85
                                               ignored_ids)
84
                                               ignored_ids)

+ 2 - 2
tracim/tracim/lib/content.py 查看文件

373
         elif new_workspace:
373
         elif new_workspace:
374
             item.workspace = new_workspace
374
             item.workspace = new_workspace
375
 
375
 
376
-        item.revision_type = ActionDescription.EDITION
376
+        item.revision_type = ActionDescription.MOVE
377
 
377
 
378
     def move_recursively(self, item: Content,
378
     def move_recursively(self, item: Content,
379
                          new_parent: Content, new_workspace: Workspace):
379
                          new_parent: Content, new_workspace: Workspace):
380
         self.move(item, new_parent, False, new_workspace)
380
         self.move(item, new_parent, False, new_workspace)
381
         self.save(item, do_notify=False)
381
         self.save(item, do_notify=False)
382
-        print('saved item #', item.content_id, new_workspace)
382
+
383
         for child in item.children:
383
         for child in item.children:
384
             self.move_recursively(child, item, new_workspace)
384
             self.move_recursively(child, item, new_workspace)
385
         return
385
         return

+ 3 - 0
tracim/tracim/lib/helpers.py 查看文件

177
     return ContentType._DELETE_LABEL[item.type]
177
     return ContentType._DELETE_LABEL[item.type]
178
 
178
 
179
 def is_item_still_editable(item):
179
 def is_item_still_editable(item):
180
+    if item.type.id != 'comment':
181
+        return False
182
+
180
     # HACK - D.A - 2014-12-24 - item contains a datetime object!!!
183
     # HACK - D.A - 2014-12-24 - item contains a datetime object!!!
181
     # 'item' is a variable which is created by serialization and it should be an instance of DictLikeClass.
184
     # 'item' is a variable which is created by serialization and it should be an instance of DictLikeClass.
182
     # therefore, it contains strins, integers and booleans (something json-ready or almost json-ready)
185
     # therefore, it contains strins, integers and booleans (something json-ready or almost json-ready)

+ 0 - 1
tracim/tracim/model/auth.py 查看文件

213
 
213
 
214
     def get_role(self, workspace: 'Workspace') -> int:
214
     def get_role(self, workspace: 'Workspace') -> int:
215
         for role in self.roles:
215
         for role in self.roles:
216
-            print('IS EQUALS ? ', role.workspace, workspace)
217
             if role.workspace == workspace:
216
             if role.workspace == workspace:
218
                 return role.role
217
                 return role.role
219
 
218
 

+ 99 - 17
tracim/tracim/model/data.py 查看文件

24
 from sqlalchemy.types import Text
24
 from sqlalchemy.types import Text
25
 from sqlalchemy.types import Unicode
25
 from sqlalchemy.types import Unicode
26
 
26
 
27
-from tg.i18n import lazy_ugettext as l_
27
+from tg.i18n import lazy_ugettext as l_, ugettext as _
28
 
28
 
29
 from tracim.model import DeclarativeBase
29
 from tracim.model import DeclarativeBase
30
 from tracim.model.auth import User
30
 from tracim.model.auth import User
169
     STATUS_UPDATE = 'status-update'
169
     STATUS_UPDATE = 'status-update'
170
     UNARCHIVING = 'unarchiving'
170
     UNARCHIVING = 'unarchiving'
171
     UNDELETION = 'undeletion'
171
     UNDELETION = 'undeletion'
172
+    MOVE = 'move'
172
 
173
 
173
     _ICONS = {
174
     _ICONS = {
174
         'archiving': 'fa fa-archive',
175
         'archiving': 'fa fa-archive',
175
         'content-comment': 'fa-comment-o',
176
         'content-comment': 'fa-comment-o',
176
         'creation': 'fa-magic',
177
         'creation': 'fa-magic',
177
-        'deletion': 'fa fa-trash',
178
-        'edition': 'fa fa-edit',
178
+        'deletion': 'fa-trash',
179
+        'edition': 'fa-edit',
179
         'revision': 'fa-history',
180
         'revision': 'fa-history',
180
         'status-update': 'fa-random',
181
         'status-update': 'fa-random',
181
-        'unarchiving': 'fa fa-file-archive-o',
182
-        'undeletion': 'fa-trash-o'
182
+        'unarchiving': 'fa-file-archive-o',
183
+        'undeletion': 'fa-trash-o',
184
+        'move': 'fa-arrows'
183
     }
185
     }
184
 
186
 
185
     _LABELS = {
187
     _LABELS = {
186
         'archiving': l_('archive'),
188
         'archiving': l_('archive'),
187
-        'content-comment': l_('commente'),
188
-        'creation': l_('creation'),
189
-        'deletion': l_('deletion'),
190
-        'edition': l_('modified'),
191
-        'revision': l_('revision'),
192
-        'status-update': l_('statut'),
193
-        'unarchiving': l_('un-archived'),
194
-        'undeletion': l_('un-deleted'),
189
+        'content-comment': l_('Item commented'),
190
+        'creation': l_('Item created'),
191
+        'deletion': l_('Item deleted'),
192
+        'edition': l_('item modified'),
193
+        'revision': l_('New revision'),
194
+        'status-update': l_('New status'),
195
+        'unarchiving': l_('Item unarchived'),
196
+        'undeletion': l_('Item undeleted'),
197
+        'move': l_('Item moved')
195
     }
198
     }
196
 
199
 
197
     def __init__(self, id):
200
     def __init__(self, id):
199
         self.id = id
202
         self.id = id
200
         self.label = ActionDescription._LABELS[id]
203
         self.label = ActionDescription._LABELS[id]
201
         self.icon = ActionDescription._ICONS[id]
204
         self.icon = ActionDescription._ICONS[id]
205
+        self.css = ''
202
 
206
 
203
     @classmethod
207
     @classmethod
204
     def allowed_values(cls):
208
     def allowed_values(cls):
210
                 cls.REVISION,
214
                 cls.REVISION,
211
                 cls.STATUS_UPDATE,
215
                 cls.STATUS_UPDATE,
212
                 cls.UNARCHIVING,
216
                 cls.UNARCHIVING,
213
-                cls.UNDELETION]
217
+                cls.UNDELETION,
218
+                cls.MOVE]
214
 
219
 
215
 
220
 
216
 class ContentStatus(object):
221
 class ContentStatus(object):
406
     def sorted(cls, types: ['ContentType']) -> ['ContentType']:
411
     def sorted(cls, types: ['ContentType']) -> ['ContentType']:
407
         return sorted(types, key=lambda content_type: content_type.priority)
412
         return sorted(types, key=lambda content_type: content_type.priority)
408
 
413
 
414
+    @property
415
+    def type(self):
416
+        return self.id
417
+
409
     def __init__(self, type):
418
     def __init__(self, type):
410
-        self.type = type
419
+        self.id = type
411
         self.icon = ContentType._CSS_ICONS[type]
420
         self.icon = ContentType._CSS_ICONS[type]
412
-        self.color = ContentType._CSS_COLORS[type]
421
+        self.color = ContentType._CSS_COLORS[type]  # deprecated
422
+        self.css = ContentType._CSS_COLORS[type]
413
         self.label = ContentType._LABEL[type]
423
         self.label = ContentType._LABEL[type]
414
         self.priority = ContentType._ORDER_WEIGHT[type]
424
         self.priority = ContentType._ORDER_WEIGHT[type]
415
 
425
 
594
         except Exception as e:
604
         except Exception as e:
595
             print(e.__str__())
605
             print(e.__str__())
596
             print('----- /*\ *****')
606
             print('----- /*\ *****')
597
-            raise ValueError('No allowed content property')
607
+            raise ValueError('Not allowed content property')
598
 
608
 
599
         return ContentType.sorted(types)
609
         return ContentType.sorted(types)
600
 
610
 
611
+    def get_history(self) -> '[VirtualEvent]':
612
+        events = []
613
+        for comment in self.get_comments():
614
+            events.append(VirtualEvent.create_from_content(comment))
615
+        for revision in self.revisions:
616
+            events.append(VirtualEvent.create_from_content_revision(revision))
617
+
618
+        sorted_events = sorted(events,
619
+                               key=lambda event: event.created, reverse=True)
620
+        return sorted_events
621
+
601
 
622
 
602
 class ContentChecker(object):
623
 class ContentChecker(object):
603
 
624
 
649
     file_mimetype = Column(Unicode(255),  unique=False, nullable=False, default='')
670
     file_mimetype = Column(Unicode(255),  unique=False, nullable=False, default='')
650
     file_content = deferred(Column(LargeBinary(), unique=False, nullable=False, default=None))
671
     file_content = deferred(Column(LargeBinary(), unique=False, nullable=False, default=None))
651
 
672
 
673
+    type = Column(Unicode(32), unique=False, nullable=False)
652
     status = Column(Unicode(32), unique=False, nullable=False)
674
     status = Column(Unicode(32), unique=False, nullable=False)
653
     created = Column(DateTime, unique=False, nullable=False)
675
     created = Column(DateTime, unique=False, nullable=False)
654
     updated = Column(DateTime, unique=False, nullable=False)
676
     updated = Column(DateTime, unique=False, nullable=False)
665
     def get_status(self):
687
     def get_status(self):
666
         return ContentStatus(self.status)
688
         return ContentStatus(self.status)
667
 
689
 
690
+    def get_label(self):
691
+        return self.label if self.label else self.file_name if self.file_name else ''
692
+
668
     def get_last_action(self) -> ActionDescription:
693
     def get_last_action(self) -> ActionDescription:
669
         return ActionDescription(self.revision_type)
694
         return ActionDescription(self.revision_type)
670
 
695
 
678
         self.node = node
703
         self.node = node
679
         self.children = children
704
         self.children = children
680
         self.is_selected = is_selected
705
         self.is_selected = is_selected
706
+
707
+class VirtualEvent(object):
708
+    @classmethod
709
+    def create_from(cls, object):
710
+        if Content == object.__class__:
711
+            return cls.create_from_content(object)
712
+        elif ContentRevisionRO == object.__class__:
713
+            return cls.create_from_content_revision(object)
714
+
715
+    @classmethod
716
+    def create_from_content(cls, content: Content):
717
+        content_type = ContentType(content.type)
718
+
719
+        label = content.get_label()
720
+        if content.type==ContentType.Comment:
721
+            label = _('<strong>{}</strong> wrote:').format(content.owner.get_display_name())
722
+
723
+        return VirtualEvent(id=content.content_id,
724
+                            created=content.created,
725
+                            owner=content.owner,
726
+                            type=content_type,
727
+                            label=label,
728
+                            content=content.description,
729
+                            ref_object=content)
730
+
731
+    @classmethod
732
+    def create_from_content_revision(cls, revision: ContentRevisionRO):
733
+        action_description = ActionDescription(revision.revision_type)
734
+
735
+        return VirtualEvent(id=revision.revision_id,
736
+                            created=revision.created,
737
+                            owner=revision.owner,
738
+                            type=action_description,
739
+                            label=action_description.label,
740
+                            content='',
741
+                            ref_object=revision)
742
+
743
+    def __init__(self, id, created, owner, type, label, content, ref_object):
744
+        self.id = id
745
+        self.created = created
746
+        self.owner = owner
747
+        self.type = type
748
+        self.label = label
749
+        self.content = content
750
+        self.ref_object = ref_object
751
+
752
+        print(type)
753
+        assert hasattr(type, 'id')
754
+        assert hasattr(type, 'css')
755
+        assert hasattr(type, 'icon')
756
+        assert hasattr(type, 'label')
757
+
758
+    def created_as_delta(self, delta_from_datetime:datetime=None):
759
+        if not delta_from_datetime:
760
+            delta_from_datetime = datetime.now()
761
+        return format_timedelta(delta_from_datetime - self.created,
762
+                                locale=tg.i18n.get_lang()[0])

+ 45 - 17
tracim/tracim/model/serializers.py 查看文件

22
 from tracim.model.data import ContentType
22
 from tracim.model.data import ContentType
23
 from tracim.model.data import RoleType
23
 from tracim.model.data import RoleType
24
 from tracim.model.data import UserRoleInWorkspace
24
 from tracim.model.data import UserRoleInWorkspace
25
+from tracim.model.data import VirtualEvent
25
 from tracim.model.data import Workspace
26
 from tracim.model.data import Workspace
26
 
27
 
27
 from tracim.model import data as pmd
28
 from tracim.model import data as pmd
64
     ADMIN_WORKSPACE = 'ADMIN_WORKSPACE'
65
     ADMIN_WORKSPACE = 'ADMIN_WORKSPACE'
65
     ADMIN_WORKSPACES = 'ADMIN_WORKSPACES'
66
     ADMIN_WORKSPACES = 'ADMIN_WORKSPACES'
66
     CONTENT_LIST = 'CONTENT_LIST'
67
     CONTENT_LIST = 'CONTENT_LIST'
68
+    CONTENT_HISTORY = 'CONTENT_HISTORY'
67
     CURRENT_USER = 'CURRENT_USER'
69
     CURRENT_USER = 'CURRENT_USER'
68
     DEFAULT = 'DEFAULT' # default context. This will allow to define a serialization method to be used by default
70
     DEFAULT = 'DEFAULT' # default context. This will allow to define a serialization method to be used by default
69
     EMAIL_NOTIFICATION = 'EMAIL_NOTIFICATION'
71
     EMAIL_NOTIFICATION = 'EMAIL_NOTIFICATION'
351
     if content.type in (ContentType.Page, ContentType.File) :
353
     if content.type in (ContentType.Page, ContentType.File) :
352
         data_container = content
354
         data_container = content
353
 
355
 
354
-
355
-
356
         # The following properties are overriden by revision values
356
         # The following properties are overriden by revision values
357
         if content.revision_to_serialize>0:
357
         if content.revision_to_serialize>0:
358
             for revision in content.revisions:
358
             for revision in content.revisions:
361
                     break
361
                     break
362
 
362
 
363
         result = DictLikeClass(
363
         result = DictLikeClass(
364
-            id = content.content_id,
365
-            parent = context.toDict(content.parent),
366
-            workspace = context.toDict(content.workspace),
367
-            type = content.type,
368
-
369
-            content = data_container.description,
370
-            created = data_container.created,
371
-            label = data_container.label,
372
-            icon = ContentType.get_icon(content.type),
373
-            owner = context.toDict(data_container.owner),
374
-            status = context.toDict(data_container.get_status()),
375
-            links = context.toDict(content.extract_links_from_content(data_container.description)),
376
-            revisions = context.toDict(sorted(content.revisions, key=lambda v: v.created, reverse=True)),
377
-            selected_revision = 'latest' if content.revision_to_serialize<=0 else content.revision_to_serialize
364
+            id=content.content_id,
365
+            parent=context.toDict(content.parent),
366
+            workspace=context.toDict(content.workspace),
367
+            type=content.type,
368
+
369
+            content=data_container.description,
370
+            created=data_container.created,
371
+            label=data_container.label,
372
+            icon=ContentType.get_icon(content.type),
373
+            owner=context.toDict(data_container.owner),
374
+            status=context.toDict(data_container.get_status()),
375
+            links=context.toDict(content.extract_links_from_content(data_container.description)),
376
+            revisions=context.toDict(sorted(content.revisions, key=lambda v: v.created, reverse=True)),
377
+            selected_revision='latest' if content.revision_to_serialize<=0 else content.revision_to_serialize,
378
+            history=Context(CTX.CONTENT_HISTORY).toDict(content.get_history())
378
         )
379
         )
379
 
380
 
380
         if content.type==ContentType.File:
381
         if content.type==ContentType.File:
395
     raise NotImplementedError
396
     raise NotImplementedError
396
 
397
 
397
 
398
 
399
+@pod_serializer(VirtualEvent, CTX.CONTENT_HISTORY)
400
+def serialize_content_for_history(event: VirtualEvent, context: Context):
401
+    urls = DictLikeClass({'delete': None})
402
+    if ContentType.Comment == event.type.id:
403
+        urls = context.toDict({
404
+          'delete': context.url('/workspaces/{wid}/folders/{fid}/{ctype}/{cid}/comments/{commentid}/put_delete'.format(
405
+              wid = event.ref_object.workspace_id,
406
+              fid=event.ref_object.parent.parent_id,
407
+              ctype=event.ref_object.parent.type+'s',
408
+              cid=event.ref_object.parent.content_id,
409
+              commentid=event.ref_object.content_id))
410
+        })
411
+
412
+    return DictLikeClass(
413
+        owner=context.toDict(event.owner),
414
+        id=event.id,
415
+        label=event.label,
416
+        type=context.toDict(event.type),
417
+        created=event.created,
418
+        created_as_delta=event.created_as_delta(),
419
+        content=event.content,
420
+        urls = urls
421
+    )
422
+
423
+
398
 @pod_serializer(Content, CTX.THREAD)
424
 @pod_serializer(Content, CTX.THREAD)
399
 def serialize_node_for_page(item: Content, context: Context):
425
 def serialize_node_for_page(item: Content, context: Context):
400
     if item.type==ContentType.Thread:
426
     if item.type==ContentType.Thread:
411
             status = context.toDict(item.get_status()),
437
             status = context.toDict(item.get_status()),
412
             type = item.type,
438
             type = item.type,
413
             workspace = context.toDict(item.workspace),
439
             workspace = context.toDict(item.workspace),
414
-            comments = reversed(context.toDict(item.get_comments()))
440
+            comments = reversed(context.toDict(item.get_comments())),
441
+            history = Context(CTX.CONTENT_HISTORY).toDict(item.get_history())
415
         )
442
         )
416
 
443
 
417
     if item.type==ContentType.Comment:
444
     if item.type==ContentType.Comment:
660
 def serialize_breadcrumb_item(content_type: ContentType, context: Context):
687
 def serialize_breadcrumb_item(content_type: ContentType, context: Context):
661
     return DictLikeClass(content_type.toDict())
688
     return DictLikeClass(content_type.toDict())
662
 
689
 
690
+
663
 @pod_serializer(Content, CTX.SEARCH)
691
 @pod_serializer(Content, CTX.SEARCH)
664
 def serialize_content_for_search_result(content: Content, context: Context):
692
 def serialize_content_for_search_result(content: Content, context: Context):
665
 
693
 

+ 5 - 1
tracim/tracim/public/assets/css/dashboard.css 查看文件

349
     padding: 0;
349
     padding: 0;
350
     position: absolute;
350
     position: absolute;
351
     top: 0;
351
     top: 0;
352
-}
352
+}
353
+
354
+#t-full-app-alert-message-id > div.alert {
355
+    box-shadow: 0px 0px 5px 5px rgba(0, 0, 0, 0.3);
356
+}

+ 5 - 21
tracim/tracim/templates/file/getone.mak 查看文件

154
     <div class="col-sm-7 col-sm-offset-3">
154
     <div class="col-sm-7 col-sm-offset-3">
155
         <div class="t-spacer-above">
155
         <div class="t-spacer-above">
156
             <span id="associated-revisions" ></span>
156
             <span id="associated-revisions" ></span>
157
-            <h4 class="anchored-title">${_('File revisions')}</h4>
157
+            <h4 class="anchored-title">${_('File history')}</h4>
158
             <div>
158
             <div>
159
                 <table class="table table-striped table-hover">
159
                 <table class="table table-striped table-hover">
160
-                    % for revid, revision in reversed(list(enumerate(reversed(result.file.revisions)))):
161
-                        <% warning_or_not = ('', 'warning')[result.file.selected_revision==revision.id] %>
162
-                        <tr class="${warning_or_not}">
163
-## FIXME - 2015-07-22 - D.A. - Do we really need to show a rev. id ?!
164
-## <td><span class="label label-default">v${revid}</span></td>
165
-                            <td class="t-less-visible">
166
-                                <span class="label label-default">${ICON.FA_FW(revision.action.icon)}
167
-                                ${revision.action.label}</span>
168
-                            </td>
169
-                            <td>${h.date(revision.created)}</td>
170
-                            <td>${h.time(revision.created)}</td>
171
-                            <td>${revision.owner.name}</td>
172
-                            <td><a href="${tg.url('/workspaces/{}/folders/{}/files/{}?revision_id={}').format(result.file.workspace.id, result.file.parent.id, result.file.id, revision.id)}">${revision.label}</a></td>
173
-                            <td class="t-less-visible" title="${_('Currently shown')}">
174
-                                % if warning_or_not:
175
-                                    ${ICON.FA_FW('fa fa-caret-left')} ${_('shown').format(result.file.selected_revision)}
176
-                                % endif
177
-                            </td>
178
-                        </tr>
160
+                    % for event in result.file.history:
161
+                        ${WIDGETS.SECURED_HISTORY_VIRTUAL_EVENT_AS_TABLE_ROW(fake_api.current_user, event, result.file.selected_revision)}
179
                     % endfor
162
                     % endfor
180
                 </table>
163
                 </table>
181
             </div>
164
             </div>
182
         </div>
165
         </div>
183
     </div>
166
     </div>
184
-<div/>
167
+<div/>
168
+

+ 4 - 20
tracim/tracim/templates/page/getone.mak 查看文件

98
     <div class="col-sm-7 col-sm-offset-3">
98
     <div class="col-sm-7 col-sm-offset-3">
99
         <div class="t-spacer-above">
99
         <div class="t-spacer-above">
100
             <span id="associated-revisions" ></span>
100
             <span id="associated-revisions" ></span>
101
-            <h4 class="anchored-title">${_('Page revisions')}</h4>
101
+            <h4 class="anchored-title">${_('Page history')}</h4>
102
             <div>
102
             <div>
103
                 <table class="table table-striped table-hover">
103
                 <table class="table table-striped table-hover">
104
-                    % for revid, revision in reversed(list(enumerate(reversed(result.page.revisions)))):
105
-                        <% warning_or_not = ('', 'warning')[result.page.selected_revision==revision.id] %>
106
-                        <tr class="${warning_or_not}">
107
-## FIXME - 2015-07-22 - D.A. - Do we really need to show a rev. id ?!
108
-## <td><span class="label label-default">v${revid}</span></td>
109
-                            <td class="t-less-visible">
110
-                                <span class="label label-default">${ICON.FA_FW(revision.action.icon)}
111
-                                ${revision.action.label}</span>
112
-                            </td>
113
-                            <td>${h.date(revision.created)}</td>
114
-                            <td>${h.time(revision.created)}</td>
115
-                            <td>${revision.owner.name}</td>
116
-                            <td><a href="${tg.url('/workspaces/{}/folders/{}/pages/{}?revision_id={}').format(result.page.workspace.id, result.page.parent.id, result.page.id, revision.id)}">${revision.label}</a></td>
117
-                            <td class="t-less-visible" title="${_('Currently shown')}">
118
-                                % if warning_or_not:
119
-                                    ${ICON.FA_FW('fa fa-caret-left')} ${_('shown').format(result.page.selected_revision)}
120
-                                % endif
121
-                            </td>
122
-                        </tr>
104
+                    % for event in result.page.history:
105
+                        ${WIDGETS.SECURED_HISTORY_VIRTUAL_EVENT_AS_TABLE_ROW(fake_api.current_user, event, result.page.selected_revision)}
123
                     % endfor
106
                     % endfor
124
                 </table>
107
                 </table>
125
             </div>
108
             </div>
126
         </div>
109
         </div>
127
     </div>
110
     </div>
128
 <div/>
111
 <div/>
112
+

+ 10 - 22
tracim/tracim/templates/thread/getone.mak 查看文件

100
     </div>
100
     </div>
101
 </div>
101
 </div>
102
 
102
 
103
-% for comment in result.thread.comments:
104
-    ${WIDGETS.SECURED_TIMELINE_ITEM(fake_api.current_user, comment)}
103
+% for event in result.thread.history:
104
+    ## TODO - D.A. - 2015-08-20
105
+    ## Allow to show full history (with status change and archive/unarchive)
106
+    % if event.type.id in ('comment', 'creation'):
107
+        ${WIDGETS.SECURED_HISTORY_VIRTUAL_EVENT(fake_api.current_user, event)}
108
+    % endif
105
 % endfor
109
 % endfor
106
 
110
 
107
-## <hr class="tracim-panel-separator"/>
108
-## <div>
109
-##     <h4 id="associated-links" class="anchored-title" >${_('Links extracted from the thread')}</h4>
110
-##     <div>
111
-##         % if len(result.thread.links)<=0:
112
-##             <p class="pod-empty">${_('No link found.')}</p>
113
-##         % else:
114
-##             <ul>
115
-##                 % for link in result.thread.links:
116
-##                     <li><a href="${link.href}">${link.label if link.label else link.href}</a></li>
117
-##                 % endfor
118
-##             </ul>
119
-##         % endif
120
-##     </div>
121
-##     <hr/>
122
-## 
123
-##     % for comment in result.thread.comments:
124
-##         ${comment}
125
-##     % endfor
126
-## </div>
111
+## % for comment in result.thread.comments:
112
+##     ${WIDGETS.SECURED_TIMELINE_ITEM(fake_api.current_user, comment)}
113
+## % endfor
114
+##

+ 76 - 10
tracim/tracim/templates/user_workspace_widgets.mak 查看文件

303
 </%def>
303
 </%def>
304
 
304
 
305
 <%def name="SECURED_TIMELINE_ITEM(user, item)">
305
 <%def name="SECURED_TIMELINE_ITEM(user, item)">
306
+##     <div class="row t-odd-or-even t-hacky-thread-comment-border-top">
307
+##         <div class="col-sm-7 col-sm-offset-3">
308
+##             <div class="t-timeline-item">
309
+## ##                <i class="fa fa-fw fa-3x fa-comment-o t-less-visible" style="margin-left: -1.5em; float:left;"></i>
310
+##                 ${ICON.FA_FW('fa fa-3x fa-comment-o t-less-visible t-timeline-item-icon')}
311
+##
312
+##                 <h5 style="margin: 0;">
313
+##                     <span class="tracim-less-visible">${_('<strong>{}</strong> wrote:').format(item.owner.name)|n}</span>
314
+##
315
+##                     <div class="pull-right text-right t-timeline-item-moment" title="${h.date_time(item.created)|n}">
316
+##                         ${_('{delta} ago').format(delta=item.created_as_delta)}
317
+##
318
+##                         % if h.is_item_still_editable(item) and item.owner.id==user.id:
319
+##                             <br/>
320
+## ##                            <div class="btn-group">
321
+##                                 <a class="t-timeline-comment-delete-button" href="${item.urls.delete}">
322
+##                                     ${_('delete')} ${ICON.FA('fa fa-trash-o')}
323
+## ##                                    ${TIM.ICO_TOOLTIP(16, 'status/user-trash-full', h.delete_label_for_item(item))}
324
+##                                 </a>
325
+## ##                            </div>
326
+##                         % endif
327
+##                     </div>
328
+##                 </h5>
329
+##                 <div class="t-timeline-item-content">
330
+##                     <div>${item.content|n}</div>
331
+##                     <br/>
332
+##                 </div>
333
+##             </div>
334
+##         </div>
335
+##     </div>
336
+</%def>
337
+
338
+<%def name="SECURED_HISTORY_VIRTUAL_EVENT(user, event)">
306
     <div class="row t-odd-or-even t-hacky-thread-comment-border-top">
339
     <div class="row t-odd-or-even t-hacky-thread-comment-border-top">
307
         <div class="col-sm-7 col-sm-offset-3">
340
         <div class="col-sm-7 col-sm-offset-3">
308
             <div class="t-timeline-item">
341
             <div class="t-timeline-item">
309
 ##                <i class="fa fa-fw fa-3x fa-comment-o t-less-visible" style="margin-left: -1.5em; float:left;"></i>
342
 ##                <i class="fa fa-fw fa-3x fa-comment-o t-less-visible" style="margin-left: -1.5em; float:left;"></i>
310
-                ${ICON.FA_FW('fa fa-3x fa-comment-o t-less-visible t-timeline-item-icon')}
343
+
344
+                ${ICON.FA_FW('fa fa-3x t-less-visible t-timeline-item-icon '+event.type.icon)}
311
 
345
 
312
                 <h5 style="margin: 0;">
346
                 <h5 style="margin: 0;">
313
-                    <span class="tracim-less-visible">${_('<strong>{}</strong> wrote:').format(item.owner.name)|n}</span>
314
 
347
 
315
-                    <div class="pull-right text-right t-timeline-item-moment" title="${h.date_time(item.created)|n}">
316
-                        ${_('{delta} ago').format(delta=item.created_as_delta)}
348
+                    % if 'comment' == event.type.id:
349
+                        <span class="tracim-less-visible">${_('<strong>{}</strong> wrote:').format(event.owner.name)|n}</span>
350
+                    %else:
351
+                        <span class="tracim-less-visible">${_('{} by <strong>{}</strong>').format(event.label, event.owner.name)|n}</span>
352
+                    % endif
353
+
354
+                    <div class="pull-right text-right t-timeline-item-moment" title="${h.date_time(event.created)|n}">
355
+                        ${_('{delta} ago').format(delta=event.created_as_delta)}
317
 
356
 
318
-                        % if h.is_item_still_editable(item) and item.owner.id==user.id:
357
+                        % if h.is_item_still_editable(event) and event.owner.id==user.id:
319
                             <br/>
358
                             <br/>
320
-##                            <div class="btn-group">
321
-                                <a class="t-timeline-comment-delete-button" href="${item.urls.delete}">
359
+                                <a class="t-timeline-comment-delete-button" href="${event.urls.delete}">
322
                                     ${_('delete')} ${ICON.FA('fa fa-trash-o')}
360
                                     ${_('delete')} ${ICON.FA('fa fa-trash-o')}
323
-##                                    ${TIM.ICO_TOOLTIP(16, 'status/user-trash-full', h.delete_label_for_item(item))}
324
                                 </a>
361
                                 </a>
325
-##                            </div>
326
                         % endif
362
                         % endif
327
                     </div>
363
                     </div>
328
                 </h5>
364
                 </h5>
329
                 <div class="t-timeline-item-content">
365
                 <div class="t-timeline-item-content">
330
-                    <div>${item.content|n}</div>
366
+                    <div>${event.content|n}</div>
331
                     <br/>
367
                     <br/>
332
                 </div>
368
                 </div>
333
             </div>
369
             </div>
334
         </div>
370
         </div>
335
     </div>
371
     </div>
336
 </%def>
372
 </%def>
373
+
374
+<%def name="SECURED_HISTORY_VIRTUAL_EVENT_AS_TABLE_ROW(user, event, current_revision_id)">
375
+    <% warning_or_not = ('', 'warning')[current_revision_id==event.id] %>
376
+    <tr class="${warning_or_not}">
377
+        <td class="t-less-visible">
378
+            <span class="label label-default">${ICON.FA_FW(event.type.icon)} ${event.type.label}</span>
379
+        </td>
380
+        <td title="${h.date_time(event.created)|n}">${_('{delta} ago').format(delta=event.created_as_delta)}</td>
381
+        <td>${event.owner.name}</td>
382
+## FIXME - REMOVE                            <td>${event}</td>
383
+
384
+        % if 'comment' == event.type.id:
385
+            <td colspan="2">
386
+                ${event.content|n}
387
+            </td>
388
+        % else:
389
+
390
+            <td>
391
+                % if event.type.id in ('creation', 'edition', 'revision'):
392
+                    <a href="${'?revision_id={}'.format(event.id)}">${_('View revision')}</a>
393
+                % endif
394
+            </td>
395
+            <td class="t-less-visible" title="${_('Currently shown')}">
396
+                % if warning_or_not:
397
+                    ${ICON.FA_FW('fa fa-caret-left')}&nbsp;${_('shown')}
398
+                % endif
399
+            </td>
400
+        % endif
401
+    </tr>
402
+</%def>