Completed
Push — develop ( 0a5c6c...9ef67f )
by Bastien
03:04 queued 30s
created

tracim_backend.config   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 725
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 38
eloc 352
dl 0
loc 725
rs 9.36
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
F CFG.__init__() 0 529 23
A PreviewDim.__repr__() 0 4 1
C CFG._set_default_app() 0 118 7
A CFG.configure_filedepot() 0 11 1
A PreviewDim.__init__() 0 3 1
A CFG.__setattr__() 0 21 5
1
# -*- coding: utf-8 -*-
2
import json
3
from urllib.parse import urlparse
4
5
import os
6
7
import typing
8
from paste.deploy.converters import asbool
9
from tracim_backend.app_models.validator import update_validators
10
from tracim_backend.extensions import app_list
11
from tracim_backend.lib.utils.logger import logger
12
from depot.manager import DepotManager
13
from tracim_backend.app_models.applications import Application
14
from tracim_backend.app_models.contents import CONTENT_TYPES
15
from tracim_backend.app_models.contents import CONTENT_STATUS
16
from tracim_backend.models.data import ActionDescription
17
18
19
class CFG(object):
20
    """Object used for easy access to config file parameters."""
21
22
    def __setattr__(self, key, value):
23
        """
24
        Log-ready setter.
25
26
        Logs all configuration parameters except password.
27
        :param key:
28
        :param value:
29
        :return:
30
        """
31
        if 'PASSWORD' not in key and \
32
                ('URL' not in key or type(value) == str) and \
33
                'CONTENT' not in key:
34
            # We do not show PASSWORD for security reason
35
            # we do not show URL because At the time of configuration setup,
36
            # it can't be evaluated
37
            # We do not show CONTENT in order not to pollute log files
38
            logger.info(self, 'CONFIG: [ {} | {} ]'.format(key, value))
39
        else:
40
            logger.info(self, 'CONFIG: [ {} | <value not shown> ]'.format(key))
41
42
        self.__dict__[key] = value
43
44
    def __init__(self, settings):
45
        """Parse configuration file."""
46
47
        ###
48
        # General
49
        ###
50
        backend_folder = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  # nopep8
51
        tracim_v2_folder = os.path.dirname(backend_folder)
52
        default_color_config_file_path = os.path.join(tracim_v2_folder, 'color.json')  # nopep8
53
        self.COLOR_CONFIG_FILE_PATH = settings.get(
54
            'color.config_file_path', default_color_config_file_path
55
        )
56
        if not os.path.exists(self.COLOR_CONFIG_FILE_PATH):
57
            raise Exception(
58
                'ERROR: {} file does not exist. '
59
                'please create it or set color.config_file_path'
60
                'with a correct value'.format(self.COLOR_CONFIG_FILE_PATH)
61
            )
62
63
        try:
64
            with open(self.COLOR_CONFIG_FILE_PATH) as json_file:
65
                self.APPS_COLORS = json.load(json_file)
66
        except Exception as e:
67
            raise Exception(
68
                'Error: {} file could not be load as json'.format(self.COLOR_CONFIG_FILE_PATH) # nopep8
69
            ) from e
70
71
        try:
72
            self.APPS_COLORS['primary']
73
        except KeyError as e:
74
            raise Exception(
75
                'Error: primary color is required in {} file'.format(
76
                    self.COLOR_CONFIG_FILE_PATH)  # nopep8
77
            ) from e
78
79
        default_enabled_app = [
80
            'contents/thread',
81
            'contents/file',
82
            'contents/html-document',
83
        ]
84
        enabled_app = []
85
        enabled_app_str = settings.get('app.enabled', None)
86
        if enabled_app_str:
87
            for app in enabled_app_str.split(','):
88
                app_name = app.strip()
89
                enabled_app.append(app_name)
90
        else:
91
            enabled_app = default_enabled_app
92
        self.ENABLED_APP = enabled_app
93
        self._set_default_app(self.ENABLED_APP)
94
        mandatory_msg = \
95
            'ERROR: {} configuration is mandatory. Set it before continuing.'
96
        self.DEPOT_STORAGE_DIR = settings.get(
97
            'depot_storage_dir',
98
        )
99
        if not self.DEPOT_STORAGE_DIR:
100
            raise Exception(
101
                mandatory_msg.format('depot_storage_dir')
102
            )
103
        self.DEPOT_STORAGE_NAME = settings.get(
104
            'depot_storage_name',
105
        )
106
        if not self.DEPOT_STORAGE_NAME:
107
            raise Exception(
108
                mandatory_msg.format('depot_storage_name')
109
            )
110
        self.PREVIEW_CACHE_DIR = settings.get(
111
            'preview_cache_dir',
112
        )
113
        if not self.PREVIEW_CACHE_DIR:
114
            raise Exception(
115
                'ERROR: preview_cache_dir configuration is mandatory. '
116
                'Set it before continuing.'
117
            )
118
119
        # TODO - G.M - 2018-09-11 - Deprecated param
120
        # self.DATA_UPDATE_ALLOWED_DURATION = int(settings.get(
121
        #     'content.update.allowed.duration',
122
        #     0,
123
        # ))
124
125
        self.API_KEY = settings.get(
126
            'api.key',
127
            ''
128
        )
129
        self.SESSION_REISSUE_TIME = int(settings.get(
130
            'session.reissue_time',
131
            120
132
        ))
133
134
        self.WEBSITE_TITLE = settings.get(
135
            'website.title',
136
            'TRACIM',
137
        )
138
139
        # base url of the frontend
140
        self.WEBSITE_BASE_URL = settings.get(
141
            'website.base_url',
142
            '',
143
        )
144
        if not self.WEBSITE_BASE_URL:
145
            raise Exception(
146
                'website.base_url is needed in order to have correct path in'
147
                'few place like in email.'
148
                'You should set it with frontend root url.'
149
            )
150
151
        self.API_BASE_URL = settings.get(
152
            'api.base_url',
153
            self.WEBSITE_BASE_URL,
154
        )
155
        allowed_origin = []
156
        allowed_origin_string = settings.get(
157
            'cors.access-control-allowed-origin',
158
            ''
159
        )
160
        if allowed_origin_string:
161
            allowed_origin.extend(allowed_origin_string.split(','))  # nopep8
162
        if not allowed_origin:
163
            allowed_origin.append(self.WEBSITE_BASE_URL)
164
            if self.API_BASE_URL != self.WEBSITE_BASE_URL:
165
                allowed_origin.append(self.API_BASE_URL)
166
        self.CORS_ALLOWED_ORIGIN = allowed_origin
167
168
        # TODO - G.M - 26-03-2018 - [Cleanup] These params seems deprecated for tracimv2,  # nopep8
169
        # Verify this
170
        #
171
        # self.WEBSITE_HOME_TITLE_COLOR = settings.get(
172
        #     'website.title.color',
173
        #     '#555',
174
        # )
175
        # self.WEBSITE_HOME_IMAGE_PATH = settings.get(
176
        #     '/assets/img/home_illustration.jpg',
177
        # )
178
        # self.WEBSITE_HOME_BACKGROUND_IMAGE_PATH = settings.get(
179
        #     '/assets/img/bg.jpg',
180
        # )
181
        #
182
        website_server_name = settings.get(
183
            'website.server_name',
184
            None,
185
        )
186
        if not website_server_name:
187
            website_server_name= urlparse(self.WEBSITE_BASE_URL).hostname
188
            logger.warning(
189
                self,
190
                'NOTE: Generated website.server_name parameter from '
191
                'website.base_url parameter -> {0}'
192
                .format(website_server_name)
193
            )
194
        self.WEBSITE_SERVER_NAME = website_server_name
195
        # TODO - G.M - 2018-09-11 - Deprecated params
196
        # self.WEBSITE_HOME_TAG_LINE = settings.get(
197
        #     'website.home.tag_line',
198
        #     '',
199
        # )
200
        # self.WEBSITE_SUBTITLE = settings.get(
201
        #     'website.home.subtitle',
202
        #     '',
203
        # )
204
        # self.WEBSITE_HOME_BELOW_LOGIN_FORM = settings.get(
205
        #     'website.home.below_login_form',
206
        #     '',
207
        # )
208
        #
209
        # self.WEBSITE_TREEVIEW_CONTENT = settings.get(
210
        #     'website.treeview.content',
211
        # )
212
213
        self.USER_AUTH_TOKEN_VALIDITY = int(settings.get(
214
            'user.auth_token.validity',
215
            '604800',
216
        ))
217
        self.USER_RESET_PASSWORD_TOKEN_VALIDITY = int(settings.get(
218
            'user.reset_password.validity',
219
            '900'
220
        ))
221
222
        self.DEBUG = asbool(settings.get('debug', False))
223
        # TODO - G.M - 27-03-2018 - [Email] Restore email config
224
        ###
225
        # EMAIL related stuff (notification, reply)
226
        ##
227
228
        self.EMAIL_NOTIFICATION_NOTIFIED_EVENTS = [
229
            ActionDescription.COMMENT,
230
            ActionDescription.CREATION,
231
            ActionDescription.EDITION,
232
            ActionDescription.REVISION,
233
            ActionDescription.STATUS_UPDATE
234
        ]
235
236
        self.EMAIL_NOTIFICATION_NOTIFIED_CONTENTS = [
237
            CONTENT_TYPES.Page.slug,
238
            CONTENT_TYPES.Thread.slug,
239
            CONTENT_TYPES.File.slug,
240
            CONTENT_TYPES.Comment.slug,
241
            # CONTENT_TYPES.Folder.slug -- Folder is skipped
242
        ]
243
        if settings.get('email.notification.from'):
244
            raise Exception(
245
                'email.notification.from configuration is deprecated. '
246
                'Use instead email.notification.from.email and '
247
                'email.notification.from.default_label.'
248
            )
249
        self.EMAIL_NOTIFICATION_FROM_EMAIL = settings.get(
250
            'email.notification.from.email',
251
            'noreply+{user_id}@trac.im'
252
        )
253
        self.EMAIL_NOTIFICATION_FROM_DEFAULT_LABEL = settings.get(
254
            'email.notification.from.default_label',
255
            'Tracim Notifications'
256
        )
257
        self.EMAIL_NOTIFICATION_REPLY_TO_EMAIL = settings.get(
258
            'email.notification.reply_to.email',
259
        )
260
        self.EMAIL_NOTIFICATION_REFERENCES_EMAIL = settings.get(
261
            'email.notification.references.email'
262
        )
263
        # Content update notification
264
        self.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_HTML = settings.get(
265
            'email.notification.content_update.template.html',
266
        )
267
        self.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_TEXT = settings.get(
268
            'email.notification.content_update.template.text',
269
        )
270
        self.EMAIL_NOTIFICATION_CONTENT_UPDATE_SUBJECT = settings.get(
271
            'email.notification.content_update.subject',
272
        )
273
        # Created account notification
274
        self.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_HTML = settings.get(
275
            'email.notification.created_account.template.html',
276
            './tracim_backend/templates/mail/created_account_body_html.mak',
277
        )
278
        self.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_TEXT = settings.get(
279
            'email.notification.created_account.template.text',
280
            './tracim_backend/templates/mail/created_account_body_text.mak',
281
        )
282
        self.EMAIL_NOTIFICATION_CREATED_ACCOUNT_SUBJECT = settings.get(
283
            'email.notification.created_account.subject',
284
            '[{website_title}] Created account',
285
        )
286
287
        # Reset password notification
288
        self.EMAIL_NOTIFICATION_RESET_PASSWORD_TEMPLATE_HTML = settings.get(
289
            'email.notification.reset_password_request.template.html',
290
            './tracim_backend/templates/mail/reset_password_body_html.mak',
291
        )
292
        self.EMAIL_NOTIFICATION_RESET_PASSWORD_TEMPLATE_TEXT = settings.get(
293
            'email.notification.reset_password_request.template.text',
294
            './tracim_backend/templates/mail/reset_password_body_text.mak',
295
        )
296
        self.EMAIL_NOTIFICATION_RESET_PASSWORD_SUBJECT = settings.get(
297
            'email.notification.reset_password_request.subject',
298
            '[{website_title}] Reset Password Request'
299
        )
300
301
        self.EMAIL_NOTIFICATION_PROCESSING_MODE = settings.get(
302
            'email.notification.processing_mode',
303
        )
304
305
        self.EMAIL_NOTIFICATION_ACTIVATED = asbool(settings.get(
306
            'email.notification.activated',
307
        ))
308
        self.EMAIL_NOTIFICATION_SMTP_SERVER = settings.get(
309
            'email.notification.smtp.server',
310
        )
311
        self.EMAIL_NOTIFICATION_SMTP_PORT = settings.get(
312
            'email.notification.smtp.port',
313
        )
314
        self.EMAIL_NOTIFICATION_SMTP_USER = settings.get(
315
            'email.notification.smtp.user',
316
        )
317
        self.EMAIL_NOTIFICATION_SMTP_PASSWORD = settings.get(
318
            'email.notification.smtp.password',
319
        )
320
        self.EMAIL_NOTIFICATION_LOG_FILE_PATH = settings.get(
321
            'email.notification.log_file_path',
322
            None,
323
        )
324
325
        # self.EMAIL_REPLY_ACTIVATED = asbool(settings.get(
326
        #     'email.reply.activated',
327
        #     False,
328
        # ))
329
        #
330
        # self.EMAIL_REPLY_IMAP_SERVER = settings.get(
331
        #     'email.reply.imap.server',
332
        # )
333
        # self.EMAIL_REPLY_IMAP_PORT = settings.get(
334
        #     'email.reply.imap.port',
335
        # )
336
        # self.EMAIL_REPLY_IMAP_USER = settings.get(
337
        #     'email.reply.imap.user',
338
        # )
339
        # self.EMAIL_REPLY_IMAP_PASSWORD = settings.get(
340
        #     'email.reply.imap.password',
341
        # )
342
        # self.EMAIL_REPLY_IMAP_FOLDER = settings.get(
343
        #     'email.reply.imap.folder',
344
        # )
345
        # self.EMAIL_REPLY_CHECK_HEARTBEAT = int(settings.get(
346
        #     'email.reply.check.heartbeat',
347
        #     60,
348
        # ))
349
        # self.EMAIL_REPLY_TOKEN = settings.get(
350
        #     'email.reply.token',
351
        # )
352
        # self.EMAIL_REPLY_IMAP_USE_SSL = asbool(settings.get(
353
        #     'email.reply.imap.use_ssl',
354
        # ))
355
        # self.EMAIL_REPLY_IMAP_USE_IDLE = asbool(settings.get(
356
        #     'email.reply.imap.use_idle',
357
        #     True,
358
        # ))
359
        # self.EMAIL_REPLY_CONNECTION_MAX_LIFETIME = int(settings.get(
360
        #     'email.reply.connection.max_lifetime',
361
        #     600,  # 10 minutes
362
        # ))
363
        # self.EMAIL_REPLY_USE_HTML_PARSING = asbool(settings.get(
364
        #     'email.reply.use_html_parsing',
365
        #     True,
366
        # ))
367
        # self.EMAIL_REPLY_USE_TXT_PARSING = asbool(settings.get(
368
        #     'email.reply.use_txt_parsing',
369
        #     True,
370
        # ))
371
        # self.EMAIL_REPLY_LOCKFILE_PATH = settings.get(
372
        #     'email.reply.lockfile_path',
373
        #     ''
374
        # )
375
        # if not self.EMAIL_REPLY_LOCKFILE_PATH and self.EMAIL_REPLY_ACTIVATED:
376
        #     raise Exception(
377
        #         mandatory_msg.format('email.reply.lockfile_path')
378
        #     )
379
        #
380
        self.EMAIL_PROCESSING_MODE = settings.get(
381
            'email.processing_mode',
382
            'sync',
383
        ).upper()
384
385
        if self.EMAIL_PROCESSING_MODE not in (
386
                self.CST.ASYNC,
387
                self.CST.SYNC,
388
        ):
389
            raise Exception(
390
                'email.processing_mode '
391
                'can ''be "{}" or "{}", not "{}"'.format(
392
                    self.CST.ASYNC,
393
                    self.CST.SYNC,
394
                    self.EMAIL_PROCESSING_MODE,
395
                )
396
            )
397
398
        self.EMAIL_SENDER_REDIS_HOST = settings.get(
399
            'email.async.redis.host',
400
            'localhost',
401
        )
402
        self.EMAIL_SENDER_REDIS_PORT = int(settings.get(
403
            'email.async.redis.port',
404
            6379,
405
        ))
406
        self.EMAIL_SENDER_REDIS_DB = int(settings.get(
407
            'email.async.redis.db',
408
            0,
409
        ))
410
411
        ###
412
        # WSGIDAV (Webdav server)
413
        ###
414
415
        # TODO - G.M - 27-03-2018 - [WebDav] Restore wsgidav config
416
        #self.WSGIDAV_CONFIG_PATH = settings.get(
417
        #    'wsgidav.config_path',
418
        #    'wsgidav.conf',
419
        #)
420
        # TODO: Convert to importlib
421
        # http://stackoverflow.com/questions/41063938/use-importlib-instead-imp-for-non-py-file
422
        #self.wsgidav_config = imp.load_source(
423
        #    'wsgidav_config',
424
        #    self.WSGIDAV_CONFIG_PATH,
425
        #)
426
        # self.WSGIDAV_PORT = self.wsgidav_config.port
427
        # self.WSGIDAV_CLIENT_BASE_URL = settings.get(
428
        #     'wsgidav.client.base_url',
429
        #     None,
430
        # )
431
        #
432
        # if not self.WSGIDAV_CLIENT_BASE_URL:
433
        #     self.WSGIDAV_CLIENT_BASE_URL = \
434
        #         '{0}:{1}'.format(
435
        #             self.WEBSITE_SERVER_NAME,
436
        #             self.WSGIDAV_PORT,
437
        #         )
438
        #     logger.warning(self,
439
        #         'NOTE: Generated wsgidav.client.base_url parameter with '
440
        #         'followings parameters: website.server_name and '
441
        #         'wsgidav.conf port'.format(
442
        #             self.WSGIDAV_CLIENT_BASE_URL,
443
        #         )
444
        #     )
445
        #
446
        # if not self.WSGIDAV_CLIENT_BASE_URL.endswith('/'):
447
        #     self.WSGIDAV_CLIENT_BASE_URL += '/'
448
449
        # TODO - G.M - 27-03-2018 - [Caldav] Restore radicale config
450
        ###
451
        # RADICALE (Caldav server)
452
        ###
453
        # self.RADICALE_SERVER_HOST = settings.get(
454
        #     'radicale.server.host',
455
        #     '127.0.0.1',
456
        # )
457
        # self.RADICALE_SERVER_PORT = int(settings.get(
458
        #     'radicale.server.port',
459
        #     5232,
460
        # ))
461
        # # Note: Other parameters needed to work in SSL (cert file, etc)
462
        # self.RADICALE_SERVER_SSL = asbool(settings.get(
463
        #     'radicale.server.ssl',
464
        #     False,
465
        # ))
466
        # self.RADICALE_SERVER_FILE_SYSTEM_FOLDER = settings.get(
467
        #     'radicale.server.filesystem.folder',
468
        # )
469
        # if not self.RADICALE_SERVER_FILE_SYSTEM_FOLDER:
470
        #     raise Exception(
471
        #         mandatory_msg.format('radicale.server.filesystem.folder')
472
        #     )
473
        # self.RADICALE_SERVER_ALLOW_ORIGIN = settings.get(
474
        #     'radicale.server.allow_origin',
475
        #     None,
476
        # )
477
        # if not self.RADICALE_SERVER_ALLOW_ORIGIN:
478
        #     self.RADICALE_SERVER_ALLOW_ORIGIN = self.WEBSITE_BASE_URL
479
        #     logger.warning(self,
480
        #         'NOTE: Generated radicale.server.allow_origin parameter with '
481
        #         'followings parameters: website.base_url ({0})'
482
        #         .format(self.WEBSITE_BASE_URL)
483
        #     )
484
        #
485
        # self.RADICALE_SERVER_REALM_MESSAGE = settings.get(
486
        #     'radicale.server.realm_message',
487
        #     'Tracim Calendar - Password Required',
488
        # )
489
        #
490
        # self.RADICALE_CLIENT_BASE_URL_HOST = settings.get(
491
        #     'radicale.client.base_url.host',
492
        #     'http://{}:{}'.format(
493
        #         self.RADICALE_SERVER_HOST,
494
        #         self.RADICALE_SERVER_PORT,
495
        #     ),
496
        # )
497
        #
498
        # self.RADICALE_CLIENT_BASE_URL_PREFIX = settings.get(
499
        #     'radicale.client.base_url.prefix',
500
        #     '/',
501
        # )
502
        # # Ensure finished by '/'
503
        # if '/' != self.RADICALE_CLIENT_BASE_URL_PREFIX[-1]:
504
        #     self.RADICALE_CLIENT_BASE_URL_PREFIX += '/'
505
        # if '/' != self.RADICALE_CLIENT_BASE_URL_PREFIX[0]:
506
        #     self.RADICALE_CLIENT_BASE_URL_PREFIX \
507
        #         = '/' + self.RADICALE_CLIENT_BASE_URL_PREFIX
508
        #
509
        # if not self.RADICALE_CLIENT_BASE_URL_HOST:
510
        #     logger.warning(self,
511
        #         'Generated radicale.client.base_url.host parameter with '
512
        #         'followings parameters: website.server_name -> {}'
513
        #         .format(self.WEBSITE_SERVER_NAME)
514
        #     )
515
        #     self.RADICALE_CLIENT_BASE_URL_HOST = self.WEBSITE_SERVER_NAME
516
        #
517
        # self.RADICALE_CLIENT_BASE_URL_TEMPLATE = '{}{}'.format(
518
        #     self.RADICALE_CLIENT_BASE_URL_HOST,
519
        #     self.RADICALE_CLIENT_BASE_URL_PREFIX,
520
        # )
521
        self.PREVIEW_JPG_RESTRICTED_DIMS = asbool(settings.get(
522
            'preview.jpg.restricted_dims', False
523
        ))
524
        preview_jpg_allowed_dims_str = settings.get('preview.jpg.allowed_dims', '')  # nopep8
525
        allowed_dims = []
526
        if preview_jpg_allowed_dims_str:
527
            for sizes in preview_jpg_allowed_dims_str.split(','):
528
                parts = sizes.split('x')
529
                assert len(parts) == 2
530
                width, height = parts
531
                assert width.isdecimal()
532
                assert height.isdecimal()
533
                size = PreviewDim(int(width), int(height))
534
                allowed_dims.append(size)
535
536
        if not allowed_dims:
537
            size = PreviewDim(256, 256)
538
            allowed_dims.append(size)
539
540
        self.PREVIEW_JPG_ALLOWED_DIMS = allowed_dims
541
542
        self.FRONTEND_SERVE = asbool(settings.get(
543
            'frontend.serve', False
544
        ))
545
        # INFO - G.M - 2018-08-06 - we pretend that frontend_dist_folder
546
        # is probably in frontend subfolder
547
        # of tracim_v2 parent of both backend and frontend
548
        backend_folder = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  # nopep8
549
        tracim_v2_folder = os.path.dirname(backend_folder)
550
        backend_i18n_folder = os.path.join(backend_folder,'tracim_backend', 'locale')  # nopep8
551
552
        self.BACKEND_I18N_FOLDER = settings.get(
553
            'backend.18n_folder_path', backend_i18n_folder
554
        )
555
        if not os.path.isdir(self.BACKEND_I18N_FOLDER):
556
            raise Exception(
557
                'ERROR: {} folder does not exist as folder. '
558
                'please set backend.i8n_folder_path'
559
                'with a correct value'.format(self.BACKEND_I18N_FOLDER)
560
            )
561
562
        frontend_dist_folder = os.path.join(tracim_v2_folder, 'frontend', 'dist')  # nopep8
563
        self.FRONTEND_DIST_FOLDER_PATH = settings.get(
564
            'frontend.dist_folder_path', frontend_dist_folder
565
        )
566
567
        # INFO - G.M - 2018-08-06 - We check dist folder existence
568
        if self.FRONTEND_SERVE and not os.path.isdir(self.FRONTEND_DIST_FOLDER_PATH):  # nopep8
569
            raise Exception(
570
                'ERROR: {} folder does not exist as folder. '
571
                'please set frontend.dist_folder.path'
572
                'with a correct value'.format(self.FRONTEND_DIST_FOLDER_PATH)
573
            )
574
575
    def configure_filedepot(self):
576
577
        # TODO - G.M - 2018-08-08 - [GlobalVar] Refactor Global var
578
        # of tracim_backend, Be careful DepotManager is a Singleton !
579
580
        depot_storage_name = self.DEPOT_STORAGE_NAME
581
        depot_storage_path = self.DEPOT_STORAGE_DIR
582
        depot_storage_settings = {'depot.storage_path': depot_storage_path}
583
        DepotManager.configure(
584
            depot_storage_name,
585
            depot_storage_settings,
586
        )
587
588
    def _set_default_app(self, enabled_app_list: typing.List[str]):
589
        # TODO - G.M - 2018-08-08 - [GlobalVar] Refactor Global var
590
        # of tracim_backend, Be careful app_list is a global_var
591
        app_list.clear()
592
        # calendar
593
        if 'calendar' in enabled_app_list:
594
            calendar = Application(
595
                label='Calendar',
596
                slug='calendar',
597
                fa_icon='calendar',
598
                is_active=False,
599
                config={},
600
                main_route='/#/workspaces/{workspace_id}/calendar',
601
                app_config=self
602
            )
603
            app_list.append(calendar)
604
605
        # thread
606
        if 'contents/thread' in enabled_app_list:
607
            thread = Application(
608
                label='Threads',
609
                slug='contents/thread',
610
                fa_icon='comments-o',
611
                is_active=True,
612
                config={},
613
                main_route='/#/workspaces/{workspace_id}/contents?type=thread',
614
                app_config=self
615
            )
616
            thread.add_content_type(
617
                slug='thread',
618
                label='Thread',
619
                creation_label='Discuss about a topic',
620
                available_statuses=CONTENT_STATUS.get_all(),
621
            )
622
            app_list.append(thread)
623
624
        # folder
625
        if 'contents/folder' in enabled_app_list :
626
            folder = Application(
627
                label='Folder',
628
                slug='contents/folder',
629
                fa_icon='folder-open-o',
630
                is_active=True,
631
                config={},
632
                main_route='',
633
                app_config=self
634
            )
635
            folder.add_content_type(
636
                slug='folder',
637
                label='Folder',
638
                creation_label='Create a folder',
639
                available_statuses=CONTENT_STATUS.get_all(),
640
                allow_sub_content=True,
641
            )
642
            app_list.append(folder)
643
644
        # file
645
        if 'contents/file' in enabled_app_list:
646
            _file = Application(
647
                label='Files',
648
                slug='contents/file',
649
                fa_icon='paperclip',
650
                is_active=True,
651
                config={},
652
                main_route='/#/workspaces/{workspace_id}/contents?type=file',
653
                app_config=self,
654
            )
655
            _file.add_content_type(
656
                slug='file',
657
                label='File',
658
                creation_label='Upload a file',
659
                available_statuses=CONTENT_STATUS.get_all(),
660
            )
661
            app_list.append(_file)
662
663
        # markdownpluspage
664
        if 'contents/markdownpluspage' in enabled_app_list:
665
            markdownpluspage = Application(
666
                label='Markdown Plus Documents',
667
                # TODO - G.M - 24-05-2018 - Check label
668
                slug='content/markdownpluspage',
669
                fa_icon='file-code-o',
670
                is_active=False,
671
                config={},
672
                main_route='/#/workspaces/{workspace_id}/contents?type=markdownpluspage',
673
                # nopep8
674
                app_config=self,
675
            )
676
            markdownpluspage.add_content_type(
677
                slug='markdownpage',
678
                label='Rich Markdown File',
679
                creation_label='Create a Markdown document',
680
                available_statuses=CONTENT_STATUS.get_all(),
681
            )
682
            app_list.append(markdownpluspage)
683
684
        if 'contents/html-document' in enabled_app_list:
685
            html_documents = Application(
686
                label='Text Documents',  # TODO - G.M - 24-05-2018 - Check label
687
                slug='contents/html-document',
688
                fa_icon='file-text-o',
689
                is_active=True,
690
                config={},
691
                main_route='/#/workspaces/{workspace_id}/contents?type=html-document',
692
                app_config=self
693
            )
694
            html_documents.add_content_type(
695
                slug='html-document',
696
                label='Text Document',
697
                creation_label='Write a document',
698
                available_statuses=CONTENT_STATUS.get_all(),
699
                slug_alias=['page']
700
            )
701
            app_list.append(html_documents)
702
703
        # TODO - G.M - 2018-08-08 - We need to update validators each time
704
        # app_list is updated.
705
        update_validators()
706
707
    class CST(object):
708
        ASYNC = 'ASYNC'
709
        SYNC = 'SYNC'
710
711
        TREEVIEW_FOLDERS = 'folders'
712
        TREEVIEW_ALL = 'all'
713
714
715
class PreviewDim(object):
716
717
    def __init__(self, width: int, height: int) -> None:
718
        self.width = width
719
        self.height = height
720
721
    def __repr__(self):
722
        return "<PreviewDim width:{width} height:{height}>".format(
723
            width=self.width,
724
            height=self.height,
725
        )
726