Browse Source

Merge pull request #209 from herawo/dev/_/185/button_to_read_all

Skylsmoi 8 years ago
parent
commit
c7409a3d81

+ 35 - 1
tracim/tracim/controllers/content.py View File

@@ -7,12 +7,15 @@ import traceback
7 7
 from cgi import FieldStorage
8 8
 import tg
9 9
 from tg import tmpl_context
10
+from tg import require
11
+from tg import predicates
10 12
 from tg.i18n import ugettext as _
11 13
 from tg.predicates import not_anonymous
12 14
 from sqlalchemy.orm.exc import NoResultFound
13 15
 from tg import abort
14 16
 
15 17
 from tracim.controllers import TIMRestController
18
+from tracim.controllers import StandardController
16 19
 from tracim.controllers import TIMRestPathContextSetup
17 20
 from tracim.controllers import TIMRestControllerWithBreadcrumb
18 21
 from tracim.controllers import TIMWorkspaceContentRestController
@@ -28,7 +31,9 @@ from tracim.lib.predicates import current_user_is_reader
28 31
 from tracim.lib.predicates import current_user_is_contributor
29 32
 from tracim.lib.predicates import current_user_is_content_manager
30 33
 from tracim.lib.predicates import require_current_user_is_owner
31
-from tracim.model.serializers import Context, CTX, DictLikeClass
34
+from tracim.model.serializers import Context
35
+from tracim.model.serializers import CTX
36
+from tracim.model.serializers import DictLikeClass
32 37
 from tracim.model.data import ActionDescription
33 38
 from tracim.model import new_revision
34 39
 from tracim.model import DBSession
@@ -1074,3 +1079,32 @@ class UserWorkspaceFolderRestController(TIMRestControllerWithBreadcrumb):
1074 1079
             msg = _('{} not un-deleted: {}').format(self._item_type_label, str(e))
1075 1080
             tg.flash(msg, CST.STATUS_ERROR)
1076 1081
             tg.redirect(back_url)
1082
+
1083
+
1084
+class ContentController(StandardController):
1085
+
1086
+    '''
1087
+    Class of controllers used for example in home to mark read the unread 
1088
+    contents via mark_all_read()
1089
+    '''
1090
+
1091
+    @classmethod
1092
+    def current_item_id_key_in_context(cls) -> str:
1093
+        return''
1094
+
1095
+    @tg.expose()
1096
+    def index(self):
1097
+        return dict()
1098
+
1099
+    @require(predicates.not_anonymous())
1100
+    @tg.expose()
1101
+    def mark_all_read(self):
1102
+        '''
1103
+        Mark as read all the content that hasn't been read
1104
+        redirects the user to "/home"
1105
+        '''
1106
+        user = tg.tmpl_context.current_user
1107
+        content_api = ContentApi(user)
1108
+        content_api.mark_read__all()
1109
+
1110
+        tg.redirect("/home")

+ 4 - 0
tracim/tracim/controllers/root.py View File

@@ -12,6 +12,7 @@ from tg import url
12 12
 
13 13
 from tg.i18n import ugettext as _
14 14
 from tracim.controllers.api import APIController
15
+from tracim.controllers.content import ContentController
15 16
 
16 17
 from tracim.lib import CST
17 18
 from tracim.lib.base import logger
@@ -57,10 +58,13 @@ class RootController(StandardController):
57 58
     debug = DebugController()
58 59
     error = ErrorController()
59 60
 
61
+
60 62
     # Rest controllers
61 63
     workspaces = UserWorkspaceRestController()
62 64
     user = UserRestController()
63 65
 
66
+    content = ContentController()
67
+
64 68
     # api
65 69
     api = APIController()
66 70
 

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

@@ -42,6 +42,19 @@ class UserWorkspaceRestController(TIMRestController):
42 42
     def get_all(self, *args, **kw):
43 43
         tg.redirect(tg.url('/home'))
44 44
 
45
+    @tg.expose()
46
+    def mark_read(self, workspace_id, **kwargs):
47
+
48
+        user = tmpl_context.current_user
49
+        workspace_api = WorkspaceApi(user)
50
+        workspace = workspace_api.get_one(workspace_id)
51
+
52
+        content_api = ContentApi(user)
53
+        content_api.mark_read__workspace(workspace)
54
+
55
+        tg.redirect('/workspaces/{}'.format(workspace_id))
56
+        return DictLikeClass(fake_api=fake_api)
57
+
45 58
     @tg.expose('tracim.templates.workspace.getone')
46 59
     def get_one(self, workspace_id, **kwargs):
47 60
         """
@@ -55,15 +68,23 @@ class UserWorkspaceRestController(TIMRestController):
55 68
         show_archived = str_as_bool(kwargs.get('show_archived', ''))
56 69
         user = tmpl_context.current_user
57 70
 
71
+        workspace_api = WorkspaceApi(user)
72
+        workspace = workspace_api.get_one(workspace_id)
73
+
74
+        unread_contents = ContentApi(user).get_last_unread(None,
75
+                                                           ContentType.Any,
76
+                                                           workspace=workspace)
58 77
         current_user_content = Context(CTX.CURRENT_USER).toDict(user)
59 78
         current_user_content.roles.sort(key=lambda role: role.workspace.name)
60 79
 
61
-        workspace_api = WorkspaceApi(user)
62
-        workspace = workspace_api.get_one(workspace_id)
80
+
63 81
 
64 82
         dictified_current_user = Context(CTX.CURRENT_USER).toDict(user)
65 83
         dictified_folders = self.folders.get_all_fake(workspace).result
66 84
         fake_api = DictLikeClass(
85
+            last_unread=Context(CTX.CONTENT_LIST).toDict(unread_contents,
86
+                                                         'contents',
87
+                                                         'nb'),
67 88
             current_user=dictified_current_user,
68 89
             current_workspace_folders=dictified_folders,
69 90
             current_user_workspace_role=workspace.get_user_role(user)

+ 25 - 1
tracim/tracim/lib/content.py View File

@@ -26,7 +26,8 @@ from sqlalchemy.sql.elements import and_
26 26
 from tracim.lib import cmp_to_key
27 27
 from tracim.lib.notifications import NotifierFactory
28 28
 from tracim.lib.utils import SameValueError
29
-from tracim.model import DBSession, new_revision
29
+from tracim.model import DBSession
30
+from tracim.model import new_revision
30 31
 from tracim.model.auth import User
31 32
 from tracim.model.data import ActionDescription
32 33
 from tracim.model.data import BreadcrumbItem
@@ -872,6 +873,29 @@ class ContentApi(object):
872 873
         content.is_deleted = False
873 874
         content.revision_type = ActionDescription.UNDELETION
874 875
 
876
+    def mark_read__all(self,
877
+                       read_datetime: datetime=None,
878
+                       do_flush: bool=True,
879
+                       recursive: bool=True
880
+                       ):
881
+
882
+        itemset = self.get_last_unread(None, ContentType.Any)
883
+
884
+        for item in itemset:
885
+            self.mark_read(item, read_datetime, do_flush, recursive)
886
+
887
+    def mark_read__workspace(self,
888
+                       workspace : Workspace,
889
+                       read_datetime: datetime=None,
890
+                       do_flush: bool=True,
891
+                       recursive: bool=True
892
+                       ):
893
+
894
+        itemset = self.get_last_unread(None, ContentType.Any, workspace)
895
+
896
+        for item in itemset:
897
+            self.mark_read(item, read_datetime, do_flush, recursive)
898
+
875 899
     def mark_read(self, content: Content,
876 900
                   read_datetime: datetime=None,
877 901
                   do_flush: bool=True, recursive: bool=True) -> Content:

+ 1 - 0
tracim/tracim/lib/webdav/sql_resources.py View File

@@ -3,6 +3,7 @@ import logging
3 3
 
4 4
 import os
5 5
 
6
+import tg
6 7
 import transaction
7 8
 import re
8 9
 from datetime import datetime

+ 2 - 0
tracim/tracim/templates/home.mak View File

@@ -90,11 +90,13 @@
90 90
 
91 91
     ## NOT READ
92 92
     <div class="" id="unread-content-panel">
93
+
93 94
         <div class="panel panel-success">
94 95
             <div class="panel-body">
95 96
                 % if fake_api.last_unread.nb <= 0:
96 97
                     ${P.EMPTY_CONTENT(_('No new content.'))}
97 98
                 % else:
99
+                    <a href="${tg.url('/content/mark_all_read')}" class="btn btn-default"> ${_('Mark everything as read')} </a>
98 100
                     <table class="table table-hover">
99 101
                         % for item in fake_api.last_unread.contents:
100 102
                             <tr>

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

@@ -54,6 +54,12 @@
54 54
 
55 55
 
56 56
 <div class="workspace__detail__wrapper">
57
+     %if fake_api.last_unread.nb > 0:
58
+         <% workspace_id = result.workspace.id %>
59
+         <a href="${tg.url('/workspaces/{ws_id}/mark_read'.format(ws_id = workspace_id))}" class="btn btn-default"> ${_('Mark this workspace as read')} </a>
60
+
61
+     %endif
62
+
57 63
     ${TITLE.H3(_('Detail'), 'fa-align-justify', 'workspace-members')}
58 64
     % if result.workspace.description:
59 65
         <p>${result.workspace.description}</p>

+ 155 - 0
tracim/tracim/tests/library/test_content_api.py View File

@@ -1,4 +1,5 @@
1 1
 # -*- coding: utf-8 -*-
2
+import datetime
2 3
 
3 4
 from nose.tools import eq_, ok_
4 5
 from nose.tools import raises
@@ -347,6 +348,160 @@ class TestContentApi(BaseTest, TestStandard):
347 348
         eq_(ActionDescription.COMMENT, c.revision_type)
348 349
 
349 350
 
351
+    def test_mark_read__workspace(self):
352
+        uapi = UserApi(None)
353
+        groups = [GroupApi(None).get_one(Group.TIM_USER),
354
+                  GroupApi(None).get_one(Group.TIM_MANAGER),
355
+                  GroupApi(None).get_one(Group.TIM_ADMIN)]
356
+        user_a = uapi.create_user(email='this.is@user',
357
+                                  groups=groups, save_now=True)
358
+        user_b = uapi.create_user(email='this.is@another.user',
359
+                                  groups=groups, save_now=True)
360
+
361
+        wapi = WorkspaceApi(user_a)
362
+        workspace1 = wapi.create_workspace(
363
+            'test workspace n°1',
364
+            save_now=True)
365
+        workspace2 = wapi.create_workspace(
366
+            'test workspace n°2',
367
+            save_now=True)
368
+
369
+        role_api1 = RoleApi(user_a)
370
+        role_api1.create_one(user_b, workspace1, UserRoleInWorkspace.READER,
371
+                            False)
372
+
373
+        role_api2 = RoleApi(user_a)
374
+        role_api2.create_one(user_b, workspace2, UserRoleInWorkspace.READER,
375
+                             False)
376
+
377
+        cont_api_a = ContentApi(user_a)
378
+        cont_api_b = ContentApi(user_b)
379
+
380
+
381
+        # Creates page_1 & page_2 in workspace 1
382
+        #     and page_3 & page_4 in workspace 2
383
+        page_1 = cont_api_a.create(ContentType.Page, workspace1, None,
384
+                                   'this is a page', do_save=True)
385
+        page_2 = cont_api_a.create(ContentType.Page, workspace1, None,
386
+                                   'this is page1', do_save=True)
387
+        page_3 = cont_api_a.create(ContentType.Thread, workspace2, None,
388
+                                   'this is page2', do_save=True)
389
+        page_4 = cont_api_a.create(ContentType.File, workspace2, None,
390
+                                   'this is page3', do_save=True)
391
+
392
+        for rev in page_1.revisions:
393
+            eq_(user_b not in rev.read_by.keys(), True)
394
+        for rev in page_2.revisions:
395
+            eq_(user_b not in rev.read_by.keys(), True)
396
+        for rev in page_3.revisions:
397
+            eq_(user_b not in rev.read_by.keys(), True)
398
+        for rev in page_4.revisions:
399
+            eq_(user_b not in rev.read_by.keys(), True)
400
+
401
+        # Set as read the workspace n°1
402
+        cont_api_b.mark_read__workspace(workspace=workspace1)
403
+
404
+        for rev in page_1.revisions:
405
+            eq_(user_b in rev.read_by.keys(), True)
406
+        for rev in page_2.revisions:
407
+            eq_(user_b in rev.read_by.keys(), True)
408
+        for rev in page_3.revisions:
409
+            eq_(user_b not in rev.read_by.keys(), True)
410
+        for rev in page_4.revisions:
411
+            eq_(user_b not in rev.read_by.keys(), True)
412
+
413
+        # Set as read the workspace n°2
414
+        cont_api_b.mark_read__workspace(workspace=workspace2)
415
+
416
+        for rev in page_1.revisions:
417
+            eq_(user_b in rev.read_by.keys(), True)
418
+        for rev in page_2.revisions:
419
+            eq_(user_b in rev.read_by.keys(), True)
420
+        for rev in page_3.revisions:
421
+            eq_(user_b in rev.read_by.keys(), True)
422
+        for rev in page_4.revisions:
423
+            eq_(user_b in rev.read_by.keys(), True)
424
+
425
+    def test_mark_read(self):
426
+        uapi = UserApi(None)
427
+        groups = [GroupApi(None).get_one(Group.TIM_USER),
428
+                  GroupApi(None).get_one(Group.TIM_MANAGER),
429
+                  GroupApi(None).get_one(Group.TIM_ADMIN)]
430
+        user_a = uapi.create_user(email='this.is@user',
431
+                                 groups=groups, save_now=True)
432
+        user_b = uapi.create_user(email='this.is@another.user',
433
+                                 groups=groups, save_now=True)
434
+
435
+        wapi = WorkspaceApi(user_a)
436
+        workspace = wapi.create_workspace(
437
+            'test workspace',
438
+            save_now=True)
439
+
440
+        role_api = RoleApi(user_a)
441
+        role_api.create_one(user_b, workspace, UserRoleInWorkspace.READER, False)
442
+        cont_api_a = ContentApi(user_a)
443
+        cont_api_b = ContentApi(user_b)
444
+
445
+        page_1 = cont_api_a.create(ContentType.Page, workspace, None,
446
+                                   'this is a page', do_save=True)
447
+
448
+        for rev in page_1.revisions:
449
+            eq_(user_b not in rev.read_by.keys(), True)
450
+
451
+        cont_api_b.mark_read(page_1)
452
+
453
+        for rev in page_1.revisions:
454
+            eq_(user_b in rev.read_by.keys(), True)
455
+
456
+
457
+    def test_mark_read__all(self):
458
+        uapi = UserApi(None)
459
+        groups = [GroupApi(None).get_one(Group.TIM_USER),
460
+                  GroupApi(None).get_one(Group.TIM_MANAGER),
461
+                  GroupApi(None).get_one(Group.TIM_ADMIN)]
462
+        user_a = uapi.create_user(email='this.is@user',
463
+                                 groups=groups, save_now=True)
464
+        user_b = uapi.create_user(email='this.is@another.user',
465
+                                 groups=groups, save_now=True)
466
+
467
+        wapi = WorkspaceApi(user_a)
468
+        workspace = wapi.create_workspace(
469
+            'test workspace',
470
+            save_now=True)
471
+
472
+        role_api = RoleApi(user_a)
473
+        role_api.create_one(user_b, workspace, UserRoleInWorkspace.READER, False)
474
+        cont_api_a = ContentApi(user_a)
475
+        cont_api_b = ContentApi(user_b)
476
+
477
+        page_2 = cont_api_a.create(ContentType.Page, workspace, None, 'this is page1', do_save=True)
478
+        page_3 = cont_api_a.create(ContentType.Thread, workspace, None, 'this is page2', do_save=True)
479
+        page_4 = cont_api_a.create(ContentType.File, workspace, None, 'this is page3', do_save=True)
480
+
481
+        for rev in page_2.revisions:
482
+            eq_(user_b not in rev.read_by.keys(), True)
483
+        for rev in page_3.revisions:
484
+            eq_(user_b not in rev.read_by.keys(), True)
485
+        for rev in page_4.revisions:
486
+            eq_(user_b not in rev.read_by.keys(), True)
487
+
488
+        DBSession.refresh(page_2)
489
+        DBSession.refresh(page_3)
490
+        DBSession.refresh(page_4)
491
+
492
+        cont_api_b.mark_read__all()
493
+
494
+        for rev in page_2.revisions:
495
+            eq_(user_b in rev.read_by.keys(), True)
496
+        for rev in page_3.revisions:
497
+            eq_(user_b in rev.read_by.keys(), True)
498
+        for rev in page_4.revisions:
499
+            eq_(user_b in rev.read_by.keys(), True)
500
+
501
+
502
+
503
+
504
+
350 505
     def test_update(self):
351 506
         uapi = UserApi(None)
352 507
         groups = [GroupApi(None).get_one(Group.TIM_USER),