Browse Source

Merge branch 'master' of github.com:tracim/tracim

Skylsmoi 8 years ago
parent
commit
99054e79b7

+ 1 - 1
tracim/development.ini.base View File

@@ -204,7 +204,7 @@ email.processing_mode = sync
204 204
 # radicale.server.host = 0.0.0.0
205 205
 # radicale.server.port = 5232
206 206
 # radicale.server.ssl = false
207
-# radicale.server.filesystem.folder = ~/.config/radicale/collections
207
+# radicale.server.filesystem.folder = ./radicale/collections
208 208
 # radicale.server.allow_origin = *
209 209
 # radicale.server.realm_message = Tracim Calendar - Password Required
210 210
 ## url can be extended like http://127.0.0.1:5232/calendar

+ 1 - 1
tracim/tracim/config/app_cfg.py View File

@@ -272,7 +272,7 @@ class CFG(object):
272 272
         self.RADICALE_SERVER_SSL = asbool(tg.config.get('radicale.server.ssl', False))
273 273
         self.RADICALE_SERVER_FILE_SYSTEM_FOLDER = tg.config.get(
274 274
             'radicale.server.filesystem.folder',
275
-            '~/.config/radicale/collections'
275
+            './radicale/collections',
276 276
         )
277 277
         self.RADICALE_SERVER_ALLOW_ORIGIN = tg.config.get(
278 278
             'radicale.server.allow_origin',

+ 2 - 0
tracim/tracim/controllers/admin/workspace.py View File

@@ -239,6 +239,8 @@ class WorkspaceRestController(TIMRestController, BaseController):
239 239
 
240 240
         if calendar_enabled:
241 241
             workspace_api_controller.ensure_calendar_exist(workspace)
242
+        else:
243
+            workspace_api_controller.disable_calendar(workspace)
242 244
 
243 245
         tg.flash(_('{} workspace updated.').format(workspace.label), CST.STATUS_OK)
244 246
         tg.redirect(self.url(workspace.workspace_id))

+ 6 - 2
tracim/tracim/controllers/content.py View File

@@ -9,6 +9,8 @@ import tg
9 9
 from tg import tmpl_context
10 10
 from tg.i18n import ugettext as _
11 11
 from tg.predicates import not_anonymous
12
+from sqlalchemy.orm.exc import NoResultFound
13
+from tg import abort
12 14
 
13 15
 from tracim.controllers import TIMRestController
14 16
 from tracim.controllers import TIMRestPathContextSetup
@@ -740,8 +742,10 @@ class UserWorkspaceFolderRestController(TIMRestControllerWithBreadcrumb):
740 742
 
741 743
     def _before(self, *args, **kw):
742 744
         TIMRestPathContextSetup.current_user()
743
-        TIMRestPathContextSetup.current_workspace()
744
-
745
+        try:
746
+            TIMRestPathContextSetup.current_workspace()
747
+        except NoResultFound:
748
+            abort(404)
745 749
 
746 750
     @tg.require(current_user_is_content_manager())
747 751
     @tg.expose('tracim.templates.folder.edit')

+ 96 - 0
tracim/tracim/lib/calendar.py View File

@@ -391,3 +391,99 @@ END:VCALENDAR
391 391
         except PutError:
392 392
             pass  # TODO BS 20161128: Radicale is down. Record this event ?
393 393
 
394
+    def get_enabled_calendar_file_path(
395
+            self,
396
+            calendar_class,
397
+            related_object_id,
398
+    ) -> str:
399
+        from tracim.config.app_cfg import CFG
400
+        cfg = CFG.get_instance()
401
+
402
+        calendar_file_name = '{}.ics'.format(related_object_id)
403
+
404
+        if calendar_class == WorkspaceCalendar:
405
+            calendar_type_folder = 'workspace'
406
+        elif calendar_class == UserCalendar:
407
+            calendar_type_folder = 'user'
408
+        else:
409
+            raise NotImplementedError()
410
+
411
+        current_calendar_file_path = os.path.join(
412
+            cfg.RADICALE_SERVER_FILE_SYSTEM_FOLDER,
413
+            calendar_type_folder,
414
+            calendar_file_name,
415
+        )
416
+
417
+        return current_calendar_file_path
418
+
419
+    def get_deleted_calendar_file_path(
420
+            self,
421
+            calendar_class,
422
+            related_object_id,
423
+    ) -> str:
424
+        from tracim.config.app_cfg import CFG
425
+        cfg = CFG.get_instance()
426
+
427
+        calendar_file_name = '{}.ics'.format(related_object_id)
428
+
429
+        if calendar_class == WorkspaceCalendar:
430
+            calendar_type_folder = 'workspace'
431
+        elif calendar_class == UserCalendar:
432
+            calendar_type_folder = 'user'
433
+        else:
434
+            raise NotImplementedError()
435
+
436
+        deleted_calendar_file_path = os.path.join(
437
+            cfg.RADICALE_SERVER_FILE_SYSTEM_FOLDER,
438
+            calendar_type_folder,
439
+            'deleted',
440
+            calendar_file_name,
441
+        )
442
+
443
+        return deleted_calendar_file_path
444
+
445
+    def disable_calendar_file(
446
+            self,
447
+            calendar_class,
448
+            related_object_id: int,
449
+            raise_: bool=False,
450
+    ) -> None:
451
+        enabled_calendar_file_path = self.get_enabled_calendar_file_path(
452
+            calendar_class,
453
+            related_object_id,
454
+        )
455
+        deleted_calendar_file_path = self.get_deleted_calendar_file_path(
456
+            calendar_class,
457
+            related_object_id,
458
+        )
459
+
460
+        deleted_folder = os.path.dirname(deleted_calendar_file_path)
461
+        if not os.path.exists(deleted_folder):
462
+            os.makedirs(deleted_folder)
463
+
464
+        try:
465
+            os.rename(enabled_calendar_file_path, deleted_calendar_file_path)
466
+        except FileNotFoundError:
467
+            if raise_:
468
+                raise
469
+
470
+    def enable_calendar_file(
471
+            self,
472
+            calendar_class,
473
+            related_object_id: int,
474
+            raise_: bool=False,
475
+    ) -> None:
476
+        enabled_calendar_file_path = self.get_enabled_calendar_file_path(
477
+            calendar_class,
478
+            related_object_id,
479
+        )
480
+        deleted_calendar_file_path = self.get_deleted_calendar_file_path(
481
+            calendar_class,
482
+            related_object_id,
483
+        )
484
+
485
+        try:
486
+            os.rename(deleted_calendar_file_path, enabled_calendar_file_path)
487
+        except FileNotFoundError:
488
+            if raise_:
489
+                raise

+ 25 - 3
tracim/tracim/lib/workspace.py View File

@@ -64,7 +64,9 @@ class WorkspaceApi(object):
64 64
             DBSession.flush()
65 65
 
66 66
         if calendar_enabled:
67
-            self.execute_created_workspace_actions(workspace)
67
+            self.ensure_calendar_exist(workspace)
68
+        else:
69
+            self.disable_calendar(workspace)
68 70
 
69 71
         return workspace
70 72
 
@@ -145,19 +147,39 @@ class WorkspaceApi(object):
145 147
         from tracim.lib.calendar import CalendarManager
146 148
         from tracim.model.organisational import WorkspaceCalendar
147 149
 
148
-        if workspace.calendar_enabled:
150
+        calendar_manager = CalendarManager(self._user)
151
+
152
+        try:
153
+            calendar_manager.enable_calendar_file(
154
+                calendar_class=WorkspaceCalendar,
155
+                related_object_id=workspace.workspace_id,
156
+                raise_=True,
157
+            )
158
+        # If previous calendar file no exist, calendar must be created
159
+        except FileNotFoundError:
149 160
             self._user.ensure_auth_token()
150 161
 
151 162
             # Ensure database is up-to-date
152 163
             DBSession.flush()
153 164
             transaction.commit()
154 165
 
155
-            calendar_manager = CalendarManager(self._user)
156 166
             calendar_manager.create_then_remove_fake_event(
157 167
                 calendar_class=WorkspaceCalendar,
158 168
                 related_object_id=workspace.workspace_id,
159 169
             )
160 170
 
171
+    def disable_calendar(self, workspace: Workspace) -> None:
172
+        # Note: Cyclic imports
173
+        from tracim.lib.calendar import CalendarManager
174
+        from tracim.model.organisational import WorkspaceCalendar
175
+
176
+        calendar_manager = CalendarManager(self._user)
177
+        calendar_manager.disable_calendar_file(
178
+            calendar_class=WorkspaceCalendar,
179
+            related_object_id=workspace.workspace_id,
180
+            raise_=False,
181
+        )
182
+
161 183
     def get_base_query(self) -> Query:
162 184
         return self._base_query()
163 185
 

+ 11 - 9
tracim/tracim/model/data.py View File

@@ -30,6 +30,15 @@ from tracim.lib.exception import ContentRevisionUpdateError
30 30
 from tracim.model import DeclarativeBase, RevisionsIntegrity
31 31
 from tracim.model.auth import User
32 32
 
33
+DEFAULT_PROPERTIES = dict(
34
+    allowed_content=dict(
35
+        folder=True,
36
+        file=True,
37
+        page=True,
38
+        thread=True,
39
+    ),
40
+)
41
+
33 42
 
34 43
 class BreadcrumbItem(object):
35 44
 
@@ -507,14 +516,7 @@ class ContentChecker(object):
507 516
     @classmethod
508 517
     def reset_properties(cls, item):
509 518
         if item.type==ContentType.Folder:
510
-            item.properties = dict(
511
-                allowed_content = dict (
512
-                    folder = True,
513
-                    file = True,
514
-                    page = True,
515
-                    thread = True
516
-                )
517
-            )
519
+            item.properties = DEFAULT_PROPERTIES
518 520
             return
519 521
 
520 522
         raise NotImplementedError
@@ -1077,7 +1079,7 @@ class Content(DeclarativeBase):
1077 1079
     def properties(self) -> dict:
1078 1080
         """ return a structure decoded from json content of _properties """
1079 1081
         if not self._properties:
1080
-            ContentChecker.reset_properties(self)
1082
+            return DEFAULT_PROPERTIES
1081 1083
         return json.loads(self._properties)
1082 1084
 
1083 1085
     @properties.setter

+ 155 - 0
tracim/tracim/tests/functional/test_calendar.py View File

@@ -312,3 +312,158 @@ END:VCALENDAR
312 312
             workspace_calendar_exist,
313 313
             'Workspace calendar should be created',
314 314
         )
315
+
316
+    def unit_test__disable_workspace_disable_file__ok__nominal_case(self):
317
+        self._connect_user(
318
+            'admin@admin.admin',
319
+            'admin@admin.admin',
320
+        )
321
+        radicale_workspaces_folder = '{0}/workspace'.format(
322
+            config.get('radicale.server.filesystem.folder'),
323
+        )
324
+        delete_radicale_workspaces_folder = '{0}/workspace/deleted'.format(
325
+            config.get('radicale.server.filesystem.folder'),
326
+        )
327
+
328
+        # Core after assume "test_created_workspace_radicale_calendar" is ok
329
+        self.app.post(
330
+            '/admin/workspaces',
331
+            OrderedDict([
332
+                ('name', 'WTESTCAL2'),
333
+                ('description', 'WTESTCAL2DESCR'),
334
+                ('calendar_enabled', 'on'),
335
+            ])
336
+        )
337
+        created_workspace = DBSession.query(Workspace)\
338
+            .filter(Workspace.label == 'WTESTCAL2')\
339
+            .one()
340
+        disable_response = self.app.put(
341
+            '/admin/workspaces/{}?_method=PUT'.format(
342
+                created_workspace.workspace_id,
343
+            ),
344
+            OrderedDict([
345
+                ('name', 'WTESTCAL2'),
346
+                ('description', 'WTESTCAL2DESCR'),
347
+                ('calendar_enabled', 'off'),
348
+            ])
349
+        )
350
+        eq_(disable_response.status_code, 302,
351
+            "Code should be 302, but is %d" % disable_response.status_code)
352
+        workspaces_calendars = [
353
+            name for name in
354
+            os.listdir(radicale_workspaces_folder)
355
+            if name.endswith('.ics')
356
+        ]
357
+        deleted_workspaces_calendars = [
358
+            name for name in
359
+            os.listdir(delete_radicale_workspaces_folder)
360
+            if name.endswith('.ics')
361
+        ]
362
+
363
+        eq_(
364
+            0,
365
+            len(workspaces_calendars),
366
+            msg='No workspace ics file should exist, but {} found'.format(
367
+                len(workspaces_calendars),
368
+            ),
369
+        )
370
+        eq_(
371
+            1,
372
+            len(deleted_workspaces_calendars),
373
+            msg='1 deleted workspace ics file should exist, but {} found'
374
+                .format(
375
+                    len(deleted_workspaces_calendars),
376
+                ),
377
+        )
378
+        workspace_ics_file_name = '{}.ics'.format(
379
+                created_workspace.workspace_id
380
+        )
381
+        ok_(
382
+            workspace_ics_file_name in deleted_workspaces_calendars,
383
+            '{} should be in deleted workspace calendar folder'.format(
384
+                workspace_ics_file_name
385
+            ),
386
+        )
387
+
388
+    def unit_test__re_enable_workspace_re_enable_file__ok__nominal_case(self):
389
+        self._connect_user(
390
+            'admin@admin.admin',
391
+            'admin@admin.admin',
392
+        )
393
+        radicale_workspaces_folder = '{0}/workspace'.format(
394
+            config.get('radicale.server.filesystem.folder'),
395
+        )
396
+        delete_radicale_workspaces_folder = '{0}/workspace/deleted'.format(
397
+            config.get('radicale.server.filesystem.folder'),
398
+        )
399
+
400
+        # Core after assume
401
+        # "unit_test__disable_workspace_disable_file__ok__nominal_case" is ok
402
+        self.app.post(
403
+            '/admin/workspaces',
404
+            OrderedDict([
405
+                ('name', 'WTESTCAL2'),
406
+                ('description', 'WTESTCAL2DESCR'),
407
+                ('calendar_enabled', 'on'),
408
+            ])
409
+        )
410
+        created_workspace = DBSession.query(Workspace) \
411
+            .filter(Workspace.label == 'WTESTCAL2') \
412
+            .one()
413
+        self.app.put(
414
+            '/admin/workspaces/{}?_method=PUT'.format(
415
+                created_workspace.workspace_id,
416
+            ),
417
+            OrderedDict([
418
+                ('name', 'WTESTCAL2'),
419
+                ('description', 'WTESTCAL2DESCR'),
420
+                ('calendar_enabled', 'off'),
421
+            ])
422
+        )
423
+        re_enable_response = self.app.put(
424
+            '/admin/workspaces/{}?_method=PUT'.format(
425
+                created_workspace.workspace_id,
426
+            ),
427
+            OrderedDict([
428
+                ('name', 'WTESTCAL2'),
429
+                ('description', 'WTESTCAL2DESCR'),
430
+                ('calendar_enabled', 'on'),
431
+            ])
432
+        )
433
+        eq_(re_enable_response.status_code, 302,
434
+            "Code should be 302, but is %d" % re_enable_response.status_code)
435
+        workspaces_calendars = [
436
+            name for name in
437
+            os.listdir(radicale_workspaces_folder)
438
+            if name.endswith('.ics')
439
+            ]
440
+        deleted_workspaces_calendars = [
441
+            name for name in
442
+            os.listdir(delete_radicale_workspaces_folder)
443
+            if name.endswith('.ics')
444
+            ]
445
+
446
+        eq_(
447
+            1,
448
+            len(workspaces_calendars),
449
+            msg='1 workspace ics file should exist, but {} found'.format(
450
+                len(workspaces_calendars),
451
+            ),
452
+        )
453
+        eq_(
454
+            0,
455
+            len(deleted_workspaces_calendars),
456
+            msg='0 deleted workspace ics file should exist, but {} found'
457
+                .format(
458
+                len(deleted_workspaces_calendars),
459
+            ),
460
+        )
461
+        workspace_ics_file_name = '{}.ics'.format(
462
+            created_workspace.workspace_id
463
+        )
464
+        ok_(
465
+            workspace_ics_file_name in workspaces_calendars,
466
+            '{} should be in workspace calendar folder'.format(
467
+                workspace_ics_file_name
468
+            ),
469
+        )