Passed
Pull Request — develop (#45)
by inkhey
01:34
created

backend.tracim_backend.views.core_api.schemas   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 1061
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 32
eloc 708
dl 0
loc 1061
rs 9.732
c 0
b 0
f 0
1
# coding=utf-8
2
import marshmallow
3
from marshmallow import post_load
4
from marshmallow.validate import Length
5
from marshmallow.validate import OneOf
6
from marshmallow.validate import Range
7
8
from tracim_backend.app_models.contents import CONTENT_STATUS
9
from tracim_backend.app_models.contents import CONTENT_TYPES
10
from tracim_backend.app_models.contents import GlobalStatus
11
from tracim_backend.app_models.contents import open_status
12
from tracim_backend.app_models.validator import all_content_types_validator
13
from tracim_backend.lib.utils.utils import DATETIME_FORMAT
14
from tracim_backend.models.auth import Group
15
from tracim_backend.models.auth import Profile
16
from tracim_backend.models.context_models import ActiveContentFilter
17
from tracim_backend.models.context_models import ResetPasswordRequest
18
from tracim_backend.models.context_models import ResetPasswordCheckToken
19
from tracim_backend.models.context_models import ResetPasswordModify
20
from tracim_backend.models.context_models import FolderContentUpdate
21
from tracim_backend.models.context_models import AutocompleteQuery
22
from tracim_backend.models.context_models import CommentCreation
23
from tracim_backend.models.context_models import CommentPath
24
from tracim_backend.models.context_models import ContentCreation
25
from tracim_backend.models.context_models import ContentFilter
26
from tracim_backend.models.context_models import ContentIdsQuery
27
from tracim_backend.models.context_models import ContentPreviewSizedPath
28
from tracim_backend.models.context_models import FileQuery
29
from tracim_backend.models.context_models import FolderContentUpdate
30
from tracim_backend.models.context_models import LoginCredentials
31
from tracim_backend.models.context_models import MoveParams
32
from tracim_backend.models.context_models import PageQuery
33
from tracim_backend.models.context_models import RevisionPreviewSizedPath
34
from tracim_backend.models.context_models import RoleUpdate
35
from tracim_backend.models.context_models import SetContentStatus
36
from tracim_backend.models.context_models import SetEmail
37
from tracim_backend.models.context_models import SetPassword
38
from tracim_backend.models.context_models import TextBasedContentUpdate
39
from tracim_backend.models.context_models import UserCreation
40
from tracim_backend.models.context_models import UserInfos
41
from tracim_backend.models.context_models import UserProfile
42
from tracim_backend.models.context_models import UserWorkspaceAndContentPath
43
from tracim_backend.models.context_models import WorkspaceAndContentPath
44
from tracim_backend.models.context_models import \
45
    WorkspaceAndContentRevisionPath
46
from tracim_backend.models.context_models import WorkspaceAndUserPath
47
from tracim_backend.models.context_models import WorkspaceMemberInvitation
48
from tracim_backend.models.context_models import WorkspaceUpdate
49
from tracim_backend.models.data import ActionDescription
50
from tracim_backend.models.data import UserRoleInWorkspace
51
52
53
class UserDigestSchema(marshmallow.Schema):
54
    """
55
    Simple user schema
56
    """
57
    user_id = marshmallow.fields.Int(dump_only=True, example=3)
58
    avatar_url = marshmallow.fields.Url(
59
        allow_none=True,
60
        example="/api/v2/asset/avatars/suri-cate.jpg",
61
        description="avatar_url is the url to the image file. "
62
                    "If no avatar, then set it to null "
63
                    "(and frontend will interpret this with a default avatar)",
64
    )
65
    public_name = marshmallow.fields.String(
66
        example='Suri Cate',
67
    )
68
69
70
class UserSchema(UserDigestSchema):
71
    """
72
    Complete user schema
73
    """
74
    email = marshmallow.fields.Email(
75
        required=True,
76
        example='[email protected]'
77
    )
78
    created = marshmallow.fields.DateTime(
79
        format=DATETIME_FORMAT,
80
        description='User account creation date',
81
    )
82
    is_active = marshmallow.fields.Bool(
83
        example=True,
84
        description='Is user account activated ?'
85
    )
86
    is_deleted = marshmallow.fields.Bool(
87
        example=False,
88
        description='Is user account deleted ?'
89
    )
90
    # TODO - G.M - 17-04-2018 - Restrict timezone values
91
    timezone = marshmallow.fields.String(
92
        description="Timezone as tz database format",
93
        example="Europe/Paris",
94
    )
95
    # TODO - G.M - 17-04-2018 - check this, relative url allowed ?
96
    caldav_url = marshmallow.fields.Url(
97
        allow_none=True,
98
        relative=True,
99
        attribute='calendar_url',
100
        example="/api/v2/calendar/user/3.ics/",
101
        description="The url for calendar CalDAV direct access",
102
    )
103
    profile = marshmallow.fields.String(
104
        attribute='profile',
105
        validate=OneOf(Profile._NAME),
106
        example='trusted-users',
107
    )
108
    lang = marshmallow.fields.String(
109
        description="User langage in iso639 format",
110
        example='en',
111
        required=False,
112
        validate=Length(min=2, max=3),
113
        allow_none=True,
114
        default=None,
115
    )
116
117
    class Meta:
118
        description = 'User account of Tracim'
119
120
121
class LoggedInUserPasswordSchema(marshmallow.Schema):
122
    loggedin_user_password = marshmallow.fields.String(
123
        required=True,
124
    )
125
126
127
class SetEmailSchema(LoggedInUserPasswordSchema):
128
    email = marshmallow.fields.Email(
129
        required=True,
130
        example='[email protected]'
131
    )
132
133
    @post_load
134
    def create_set_email_object(self, data):
135
        return SetEmail(**data)
136
137
138
class SetPasswordSchema(LoggedInUserPasswordSchema):
139
    new_password = marshmallow.fields.String(
140
        example='8QLa$<w',
141
        required=True
142
    )
143
    new_password2 = marshmallow.fields.String(
144
        example='8QLa$<w',
145
        required=True
146
    )
147
148
    @post_load
149
    def create_set_password_object(self, data):
150
        return SetPassword(**data)
151
152
153
class UserInfosSchema(marshmallow.Schema):
154
    timezone = marshmallow.fields.String(
155
        description="Timezone as tz database format",
156
        example="Europe/Paris",
157
        required=True,
158
    )
159
    public_name = marshmallow.fields.String(
160
        example='Suri Cate',
161
        required=True,
162
    )
163
    lang = marshmallow.fields.String(
164
        description="User langage in iso639 format",
165
        example='en',
166
        required=True,
167
        validate=Length(min=2, max=3),
168
        allow_none=True,
169
        default=None,
170
    )
171
172
    @post_load
173
    def create_user_info_object(self, data):
174
        return UserInfos(**data)
175
176
177
class UserProfileSchema(marshmallow.Schema):
178
    profile = marshmallow.fields.String(
179
        attribute='profile',
180
        validate=OneOf(Profile._NAME),
181
        example='trusted-users',
182
    )
183
    @post_load
184
    def create_user_profile(self, data):
185
        return UserProfile(**data)
186
187
188
class UserCreationSchema(marshmallow.Schema):
189
    email = marshmallow.fields.Email(
190
        required=True,
191
        example='[email protected]'
192
    )
193
    password = marshmallow.fields.String(
194
        example='8QLa$<w',
195
        required=False,
196
    )
197
    profile = marshmallow.fields.String(
198
        attribute='profile',
199
        validate=OneOf(Profile._NAME),
200
        example='trusted-users',
201
        required=False,
202
        default=Group.TIM_USER_GROUPNAME
203
    )
204
    timezone = marshmallow.fields.String(
205
        description="Timezone as tz database format",
206
        example="Europe/Paris",
207
        required=False,
208
        default=''
209
    )
210
    public_name = marshmallow.fields.String(
211
        example='Suri Cate',
212
        required=False,
213
        default=None,
214
    )
215
    lang = marshmallow.fields.String(
216
        description="User langage in iso639 format",
217
        example='en',
218
        required=False,
219
        validate=Length(min=2, max=3),
220
        allow_none=True,
221
        default=None,
222
    )
223
    email_notification = marshmallow.fields.Bool(
224
        example=True,
225
        required=False,
226
        default=True,
227
    )
228
229
    @post_load
230
    def create_user(self, data):
231
        return UserCreation(**data)
232
233
234
# Path Schemas
235
236
class UserIdPathSchema(marshmallow.Schema):
237
    user_id = marshmallow.fields.Int(
238
        example=3,
239
        required=True,
240
        description='id of a valid user',
241
        validate=Range(min=1, error="Value must be greater than 0"),
242
    )
243
244
245
class WorkspaceIdPathSchema(marshmallow.Schema):
246
    workspace_id = marshmallow.fields.Int(
247
        example=4,
248
        required=True,
249
        description='id of a valid workspace',
250
        validate=Range(min=1, error="Value must be greater than 0"),
251
    )
252
253
254
class ContentIdPathSchema(marshmallow.Schema):
255
    content_id = marshmallow.fields.Int(
256
        example=6,
257
        required=True,
258
        description='id of a valid content',
259
        validate=Range(min=1, error="Value must be greater than 0"),
260
    )
261
262
263
class RevisionIdPathSchema(marshmallow.Schema):
264
    revision_id = marshmallow.fields.Int(example=6, required=True)
265
266
267
class WorkspaceAndUserIdPathSchema(
268
    UserIdPathSchema,
269
    WorkspaceIdPathSchema
270
):
271
    @post_load
272
    def make_path_object(self, data):
273
        return WorkspaceAndUserPath(**data)
274
275
276
class WorkspaceAndContentIdPathSchema(
277
    WorkspaceIdPathSchema,
278
    ContentIdPathSchema
279
):
280
    @post_load
281
    def make_path_object(self, data):
282
        return WorkspaceAndContentPath(**data)
283
284
285
class WidthAndHeightPathSchema(marshmallow.Schema):
286
    width = marshmallow.fields.Int(example=256)
287
    height = marshmallow.fields.Int(example=256)
288
289
290
class AllowedJpgPreviewSizesSchema(marshmallow.Schema):
291
    width = marshmallow.fields.Int(example=256)
292
    height = marshmallow.fields.Int(example=256)
293
294
295
class AllowedJpgPreviewDimSchema(marshmallow.Schema):
296
    restricted = marshmallow.fields.Bool()
297
    dimensions = marshmallow.fields.Nested(
298
        AllowedJpgPreviewSizesSchema,
299
        many=True
300
    )
301
302
303
class WorkspaceAndContentRevisionIdPathSchema(
304
    WorkspaceIdPathSchema,
305
    ContentIdPathSchema,
306
    RevisionIdPathSchema,
307
):
308
    @post_load
309
    def make_path_object(self, data):
310
        return WorkspaceAndContentRevisionPath(**data)
311
312
313
class ContentPreviewSizedPathSchema(
314
    WorkspaceAndContentIdPathSchema,
315
    WidthAndHeightPathSchema
316
):
317
    @post_load
318
    def make_path_object(self, data):
319
        return ContentPreviewSizedPath(**data)
320
321
322
class RevisionPreviewSizedPathSchema(
323
    WorkspaceAndContentRevisionIdPathSchema,
324
    WidthAndHeightPathSchema
325
):
326
    @post_load
327
    def make_path_object(self, data):
328
        return RevisionPreviewSizedPath(**data)
329
330
331
class UserWorkspaceAndContentIdPathSchema(
332
    UserIdPathSchema,
333
    WorkspaceIdPathSchema,
334
    ContentIdPathSchema,
335
):
336
    @post_load
337
    def make_path_object(self, data):
338
        return UserWorkspaceAndContentPath(**data)
339
340
341
class UserWorkspaceIdPathSchema(
342
    UserIdPathSchema,
343
    WorkspaceIdPathSchema,
344
):
345
    @post_load
346
    def make_path_object(self, data):
347
        return WorkspaceAndUserPath(**data)
348
349
350
class CommentsPathSchema(WorkspaceAndContentIdPathSchema):
351
    comment_id = marshmallow.fields.Int(
352
        example=6,
353
        description='id of a valid comment related to content content_id',
354
        required=True,
355
        validate=Range(min=1, error="Value must be greater than 0"),
356
    )
357
358
    @post_load
359
    def make_path_object(self, data):
360
        return CommentPath(**data)
361
362
363
class AutocompleteQuerySchema(marshmallow.Schema):
364
    acp = marshmallow.fields.Str(
365
        example='test',
366
        description='search text to query',
367
        validate=Length(min=2),
368
        required=True,
369
    )
370
    @post_load
371
    def make_autocomplete(self, data):
372
        return AutocompleteQuery(**data)
373
374
375
class FileQuerySchema(marshmallow.Schema):
376
    force_download = marshmallow.fields.Int(
377
        example=1,
378
        default=0,
379
        description='force download of file or let browser decide if'
380
                    'file can be read directly from browser',
381
        validate=Range(min=0, max=1, error="Value must be 0 or 1"),
382
    )
383
384
    @post_load
385
    def make_query(self, data):
386
        return FileQuery(**data)
387
388
389
class PageQuerySchema(FileQuerySchema):
390
    page = marshmallow.fields.Int(
391
        example=2,
392
        default=1,
393
        description='allow to show a specific page of a pdf file',
394
        validate=Range(min=1, error="Value must be positive"),
395
    )
396
397
    @post_load
398
    def make_query(self, data):
399
        return PageQuery(**data)
400
401
402
class FilterContentQuerySchema(marshmallow.Schema):
403
    parent_id = marshmallow.fields.Int(
404
        example=2,
405
        default=0,
406
        description='allow to filter items in a folder.'
407
                    ' If not set, then return all contents.'
408
                    ' If set to 0, then return root contents.'
409
                    ' If set to another value, return all contents'
410
                    ' directly included in the folder parent_id',
411
        validate=Range(min=0, error="Value must be positive or 0"),
412
    )
413
    show_archived = marshmallow.fields.Int(
414
        example=0,
415
        default=0,
416
        description='if set to 1, then show archived contents.'
417
                    ' Default is 0 - hide archived content',
418
        validate=Range(min=0, max=1, error="Value must be 0 or 1"),
419
    )
420
    show_deleted = marshmallow.fields.Int(
421
        example=0,
422
        default=0,
423
        description='if set to 1, then show deleted contents.'
424
                    ' Default is 0 - hide deleted content',
425
        validate=Range(min=0, max=1, error="Value must be 0 or 1"),
426
    )
427
    show_active = marshmallow.fields.Int(
428
        example=1,
429
        default=1,
430
        description='f set to 1, then show active contents. '
431
                    'Default is 1 - show active content.'
432
                    ' Note: active content are content '
433
                    'that is neither archived nor deleted. '
434
                    'The reason for this parameter to exist is for example '
435
                    'to allow to show only archived documents',
436
        validate=Range(min=0, max=1, error="Value must be 0 or 1"),
437
    )
438
    content_type = marshmallow.fields.String(
439
        example=CONTENT_TYPES.Any_SLUG,
440
        default=CONTENT_TYPES.Any_SLUG,
441
        validate=all_content_types_validator
442
    )
443
    label = marshmallow.fields.String(
444
        example='myfilename',
445
        default=None,
446
        allow_none=True,
447
        description='Filter by content label'
448
    )
449
450
    @post_load
451
    def make_content_filter(self, data):
452
        return ContentFilter(**data)
453
454
455
class ActiveContentFilterQuerySchema(marshmallow.Schema):
456
    limit = marshmallow.fields.Int(
457
        example=2,
458
        default=0,
459
        description='if 0 or not set, return all elements, else return only '
460
                    'the first limit elem (according to offset)',
461
        validate=Range(min=0, error="Value must be positive or 0"),
462
    )
463
    before_content_id = marshmallow.fields.Int(
464
        example=41,
465
        default=None,
466
        allow_none=True,
467
        description='return only content updated before this content',
468
    )
469
    @post_load
470
    def make_content_filter(self, data):
471
        return ActiveContentFilter(**data)
472
473
474
class ContentIdsQuerySchema(marshmallow.Schema):
475
    contents_ids = marshmallow.fields.List(
476
        marshmallow.fields.Int(
477
            example=6,
478
            validate=Range(min=1, error="Value must be greater than 0"),
479
        )
480
    )
481
    @post_load
482
    def make_contents_ids(self, data):
483
        return ContentIdsQuery(**data)
484
485
486
###
487
488
489
class RoleUpdateSchema(marshmallow.Schema):
490
    role = marshmallow.fields.String(
491
        required=True,
492
        example='contributor',
493
        validate=OneOf(UserRoleInWorkspace.get_all_role_slug())
494
    )
495
496
    @post_load
497
    def make_role(self, data):
498
        return RoleUpdate(**data)
499
500
501
class WorkspaceMemberInviteSchema(marshmallow.Schema):
502
    role = marshmallow.fields.String(
503
        example='contributor',
504
        validate=OneOf(UserRoleInWorkspace.get_all_role_slug())
505
    )
506
    user_id = marshmallow.fields.Int(
507
        example=5,
508
        default=None,
509
        allow_none=True,
510
    )
511
    user_email_or_public_name = marshmallow.fields.String(
512
        example='[email protected]',
513
        default=None,
514
        allow_none=True,
515
    )
516
517
    @post_load
518
    def make_role(self, data):
519
        return WorkspaceMemberInvitation(**data)
520
521
522
class ResetPasswordRequestSchema(marshmallow.Schema):
523
    email = marshmallow.fields.Email(
524
        required=True,
525
        example='[email protected]'
526
    )
527
528
    @post_load
529
    def make_object(self, data):
530
        return ResetPasswordRequest(**data)
531
532
533
class ResetPasswordCheckTokenSchema(marshmallow.Schema):
534
    email = marshmallow.fields.Email(
535
        required=True,
536
        example='[email protected]'
537
    )
538
    reset_password_token = marshmallow.fields.String(
539
        description="token to reset password of given user",
540
        required=True,
541
    )
542
543
    @post_load
544
    def make_object(self, data):
545
        return ResetPasswordCheckToken(**data)
546
547
548
class ResetPasswordModifySchema(marshmallow.Schema):
549
    email = marshmallow.fields.Email(
550
        required=True,
551
        example='[email protected]'
552
    )
553
    reset_password_token = marshmallow.fields.String(
554
        description="token to reset password of given user",
555
        required=True,
556
    )
557
    new_password = marshmallow.fields.String(
558
        example='8QLa$<w',
559
        required=True
560
    )
561
    new_password2 = marshmallow.fields.String(
562
        example='8QLa$<w',
563
        required=True
564
    )
565
566
    @post_load
567
    def make_object(self, data):
568
        return ResetPasswordModify(**data)
569
570
571
class BasicAuthSchema(marshmallow.Schema):
572
573
    email = marshmallow.fields.Email(
574
        example='[email protected]',
575
        required=True
576
    )
577
    password = marshmallow.fields.String(
578
        example='8QLa$<w',
579
        required=True,
580
        load_only=True,
581
    )
582
583
    class Meta:
584
        description = 'Entry for HTTP Basic Auth'
585
586
    @post_load
587
    def make_login(self, data):
588
        return LoginCredentials(**data)
589
590
591
class LoginOutputHeaders(marshmallow.Schema):
592
    expire_after = marshmallow.fields.String()
593
594
595
class WorkspaceModifySchema(marshmallow.Schema):
596
    label = marshmallow.fields.String(
597
        required=True,
598
        example='My Workspace',
599
    )
600
    description = marshmallow.fields.String(
601
        required=True,
602
        example='A super description of my workspace.',
603
    )
604
605
    @post_load
606
    def make_workspace_modifications(self, data):
607
        return WorkspaceUpdate(**data)
608
609
610
class WorkspaceCreationSchema(WorkspaceModifySchema):
611
    pass
612
613
614
class NoContentSchema(marshmallow.Schema):
615
616
    class Meta:
617
        description = 'Empty Schema'
618
    pass
619
620
621
class WorkspaceMenuEntrySchema(marshmallow.Schema):
622
    slug = marshmallow.fields.String(example='markdown-pages')
623
    label = marshmallow.fields.String(example='Markdown Documents')
624
    route = marshmallow.fields.String(
625
        example='/#/workspace/{workspace_id}/contents/?type=mardown-page',
626
        description='the route is the frontend route. '
627
                    'It may include workspace_id '
628
                    'which must be replaced on backend size '
629
                    '(the route must be ready-to-use)'
630
    )
631
    fa_icon = marshmallow.fields.String(
632
        example='file-text-o',
633
        description='CSS class of the icon. Example: file-o for using Fontawesome file-text-o icon',  # nopep8
634
    )
635
    hexcolor = marshmallow.fields.String(
636
        example='#F0F9DC',
637
        description='Hexadecimal color of the entry.'
638
    )
639
640
    class Meta:
641
        description = 'Entry element of a workspace menu'
642
643
644
class WorkspaceDigestSchema(marshmallow.Schema):
645
    workspace_id = marshmallow.fields.Int(
646
        example=4,
647
        validate=Range(min=1, error="Value must be greater than 0"),
648
    )
649
    slug = marshmallow.fields.String(example='intranet')
650
    label = marshmallow.fields.String(example='Intranet')
651
    sidebar_entries = marshmallow.fields.Nested(
652
        WorkspaceMenuEntrySchema,
653
        many=True,
654
    )
655
    is_deleted = marshmallow.fields.Bool(example=False, default=False)
656
657
    class Meta:
658
        description = 'Digest of workspace informations'
659
660
661
class WorkspaceSchema(WorkspaceDigestSchema):
662
    description = marshmallow.fields.String(example='All intranet data.')
663
664
    class Meta:
665
        description = 'Full workspace informations'
666
667
668
class WorkspaceMemberSchema(marshmallow.Schema):
669
    role = marshmallow.fields.String(
670
        example='contributor',
671
        validate=OneOf(UserRoleInWorkspace.get_all_role_slug())
672
    )
673
    user_id = marshmallow.fields.Int(
674
        example=3,
675
        validate=Range(min=1, error="Value must be greater than 0"),
676
    )
677
    workspace_id = marshmallow.fields.Int(
678
        example=4,
679
        validate=Range(min=1, error="Value must be greater than 0"),
680
    )
681
    user = marshmallow.fields.Nested(
682
        UserDigestSchema()
683
    )
684
    workspace = marshmallow.fields.Nested(
685
        WorkspaceDigestSchema(exclude=('sidebar_entries',))
686
    )
687
    is_active = marshmallow.fields.Bool()
688
    do_notify = marshmallow.fields.Bool(
689
        description='has user enabled notification for this workspace',
690
        example=True,
691
    )
692
693
    class Meta:
694
        description = 'Workspace Member information'
695
696
697
class WorkspaceMemberCreationSchema(WorkspaceMemberSchema):
698
    newly_created = marshmallow.fields.Bool(
699
        exemple=False,
700
        description='Is the user completely new '
701
                    '(and account was just created) or not ?',
702
    )
703
    email_sent = marshmallow.fields.Bool(
704
        exemple=False,
705
        description='Has an email been sent to user to inform him about '
706
                    'this new workspace registration and eventually his account'
707
                    'creation'
708
    )
709
710
711
class ApplicationConfigSchema(marshmallow.Schema):
712
    pass
713
    #  TODO - G.M - 24-05-2018 - Set this
714
715
716
class TimezoneSchema(marshmallow.Schema):
717
    name = marshmallow.fields.String(example='Europe/London')
718
719
720
class AboutSchema(marshmallow.Schema):
721
    name = marshmallow.fields.String(example='Tracim', description='Software name')  # nopep8
722
    version = marshmallow.fields.String(example='2.0', allow_none=True, description='Version of Tracim')  # nopep8
723
    datetime = marshmallow.fields.DateTime(format=DATETIME_FORMAT)
724
    website = marshmallow.fields.URL(allow_none=True)
725
726
727
class ConfigSchema(marshmallow.Schema):
728
    email_notification_activated = marshmallow.fields.Bool()
729
730
731
class ApplicationSchema(marshmallow.Schema):
732
    label = marshmallow.fields.String(example='Calendar')
733
    slug = marshmallow.fields.String(example='calendar')
734
    fa_icon = marshmallow.fields.String(
735
        example='file-o',
736
        description='CSS class of the icon. Example: file-o for using Fontawesome file-o icon',  # nopep8
737
    )
738
    hexcolor = marshmallow.fields.String(
739
        example='#FF0000',
740
        description='HTML encoded color associated to the application. Example:#FF0000 for red'  # nopep8
741
    )
742
    is_active = marshmallow.fields.Boolean(
743
        example=True,
744
        description='if true, the application is in use in the context',
745
    )
746
    config = marshmallow.fields.Nested(
747
        ApplicationConfigSchema,
748
    )
749
750
    class Meta:
751
        description = 'Tracim Application informations'
752
753
754
class StatusSchema(marshmallow.Schema):
755
    slug = marshmallow.fields.String(
756
        example='open',
757
        description='the slug represents the type of status. '
758
                    'Statuses are open, closed-validated, closed-invalidated, closed-deprecated'  # nopep8
759
    )
760
    global_status = marshmallow.fields.String(
761
        example='open',
762
        description='global_status: open, closed',
763
        validate=OneOf([status.value for status in GlobalStatus]),
764
    )
765
    label = marshmallow.fields.String(example='Open')
766
    fa_icon = marshmallow.fields.String(example='fa-check')
767
    hexcolor = marshmallow.fields.String(example='#0000FF')
768
769
770
class ContentTypeSchema(marshmallow.Schema):
771
    slug = marshmallow.fields.String(
772
        example='pagehtml',
773
        validate=all_content_types_validator,
774
    )
775
    fa_icon = marshmallow.fields.String(
776
        example='fa-file-text-o',
777
        description='CSS class of the icon. Example: file-o for using Fontawesome file-o icon',  # nopep8
778
    )
779
    hexcolor = marshmallow.fields.String(
780
        example="#FF0000",
781
        description='HTML encoded color associated to the application. Example:#FF0000 for red'  # nopep8
782
    )
783
    label = marshmallow.fields.String(
784
        example='Text Documents'
785
    )
786
    creation_label = marshmallow.fields.String(
787
        example='Write a document'
788
    )
789
    available_statuses = marshmallow.fields.Nested(
790
        StatusSchema,
791
        many=True
792
    )
793
794
795
class ContentMoveSchema(marshmallow.Schema):
796
    # TODO - G.M - 30-05-2018 - Read and apply this note
797
    # Note:
798
    # if the new workspace is different, then the backend
799
    # must check if the user is allowed to move to this workspace
800
    # (the user must be content manager of both workspaces)
801
    new_parent_id = marshmallow.fields.Int(
802
        example=42,
803
        description='id of the new parent content id.',
804
        allow_none=True,
805
        required=True,
806
        validate=Range(min=0, error="Value must be positive or 0"),
807
    )
808
    new_workspace_id = marshmallow.fields.Int(
809
        example=2,
810
        description='id of the new workspace id.',
811
        required=True,
812
        validate=Range(min=1, error="Value must be greater than 0"),
813
    )
814
815
    @post_load
816
    def make_move_params(self, data):
817
        return MoveParams(**data)
818
819
820
class ContentCreationSchema(marshmallow.Schema):
821
    label = marshmallow.fields.String(
822
        required=True,
823
        example='contract for client XXX',
824
        description='Title of the content to create',
825
        validate=Length(min=1),
826
        required=True
827
    )
828
    content_type = marshmallow.fields.String(
829
        required=True,
830
        example='html-document',
831
        validate=all_content_types_validator,
832
        required=True,
833
    )
834
    parent_id = marshmallow.fields.Integer(
835
        example=35,
836
        description='content_id of parent content, if content should be placed in a folder, this should be folder content_id.', # nopep8
837
        allow_none=True,
838
        default=None,
839
        validate=Range(min=1, error="Value must be positive"),
840
    )
841
842
843
    @post_load
844
    def make_content_creation(self, data):
845
        return ContentCreation(**data)
846
847
848
class ContentDigestSchema(marshmallow.Schema):
849
    content_id = marshmallow.fields.Int(
850
        example=6,
851
        validate=Range(min=1, error="Value must be greater than 0"),
852
    )
853
    slug = marshmallow.fields.Str(example='intervention-report-12')
854
    parent_id = marshmallow.fields.Int(
855
        example=34,
856
        allow_none=True,
857
        default=None,
858
        validate=Range(min=0, error="Value must be positive or 0"),
859
    )
860
    workspace_id = marshmallow.fields.Int(
861
        example=19,
862
        validate=Range(min=1, error="Value must be greater than 0"),
863
    )
864
    label = marshmallow.fields.Str(example='Intervention Report 12')
865
    content_type = marshmallow.fields.Str(
866
        example='html-document',
867
        validate=all_content_types_validator,
868
    )
869
    sub_content_types = marshmallow.fields.List(
870
        marshmallow.fields.String(
871
            example='html-content',
872
            validate=all_content_types_validator
873
        ),
874
        description='list of content types allowed as sub contents. '
875
                    'This field is required for folder contents, '
876
                    'set it to empty list in other cases'
877
    )
878
    status = marshmallow.fields.Str(
879
        example='closed-deprecated',
880
        validate=OneOf(CONTENT_STATUS.get_all_slugs_values()),
881
        description='this slug is found in content_type available statuses',
882
        default=open_status
883
    )
884
    is_archived = marshmallow.fields.Bool(example=False, default=False)
885
    is_deleted = marshmallow.fields.Bool(example=False, default=False)
886
    show_in_ui = marshmallow.fields.Bool(
887
        example=True,
888
        description='if false, then do not show content in the treeview. '
889
                    'This may his maybe used for specific contents or '
890
                    'for sub-contents. Default is True. '
891
                    'In first version of the API, this field is always True',
892
    )
893
894
895
class ReadStatusSchema(marshmallow.Schema):
896
    content_id = marshmallow.fields.Int(
897
        example=6,
898
        validate=Range(min=1, error="Value must be greater than 0"),
899
    )
900
    read_by_user = marshmallow.fields.Bool(example=False, default=False)
901
#####
902
# Content
903
#####
904
905
906
class ContentSchema(ContentDigestSchema):
907
    current_revision_id = marshmallow.fields.Int(example=12)
908
    created = marshmallow.fields.DateTime(
909
        format=DATETIME_FORMAT,
910
        description='Content creation date',
911
    )
912
    author = marshmallow.fields.Nested(UserDigestSchema)
913
    modified = marshmallow.fields.DateTime(
914
        format=DATETIME_FORMAT,
915
        description='date of last modification of content',
916
    )
917
    last_modifier = marshmallow.fields.Nested(UserDigestSchema)
918
919
920
class TextBasedDataAbstractSchema(marshmallow.Schema):
921
    raw_content = marshmallow.fields.String(
922
        description='Content of the object, may be raw text or <b>html</b> for example'  # nopep8
923
    )
924
925
926
class FileInfoAbstractSchema(marshmallow.Schema):
927
    raw_content = marshmallow.fields.String(
928
        description='raw text or html description of the file'
929
    )
930
    page_nb = marshmallow.fields.Int(
931
        description='number of pages, return null value if unaivalable',
932
        example=1,
933
        allow_none=True,
934
    )
935
    mimetype = marshmallow.fields.String(
936
        description='file content mimetype',
937
        example='image/jpeg',
938
        required=True,
939
    )
940
    size = marshmallow.fields.Int(
941
        description='file size in byte, return null value if unaivalable',
942
        example=1024,
943
        allow_none=True,
944
    )
945
    pdf_available = marshmallow.fields.Bool(
946
        description="Is pdf version of file available ?",
947
        example=True,
948
    )
949
950
951
class TextBasedContentSchema(ContentSchema, TextBasedDataAbstractSchema):
952
    pass
953
954
955
class FileContentSchema(ContentSchema, FileInfoAbstractSchema):
956
    pass
957
958
#####
959
# Revision
960
#####
961
962
963
class RevisionSchema(ContentDigestSchema):
964
    comment_ids = marshmallow.fields.List(
965
        marshmallow.fields.Int(
966
            example=4,
967
            validate=Range(min=1, error="Value must be greater than 0"),
968
        )
969
    )
970
    revision_id = marshmallow.fields.Int(
971
        example=12,
972
        validate=Range(min=1, error="Value must be greater than 0"),
973
    )
974
    revision_type = marshmallow.fields.String(
975
        example=ActionDescription.CREATION,
976
        validate=OneOf(ActionDescription.allowed_values()),
977
    )
978
    created = marshmallow.fields.DateTime(
979
        format=DATETIME_FORMAT,
980
        description='Content creation date',
981
    )
982
    author = marshmallow.fields.Nested(UserDigestSchema)
983
984
985
class TextBasedRevisionSchema(RevisionSchema, TextBasedDataAbstractSchema):
986
    pass
987
988
989
class FileRevisionSchema(RevisionSchema, FileInfoAbstractSchema):
990
    pass
991
992
993
class CommentSchema(marshmallow.Schema):
994
    content_id = marshmallow.fields.Int(
995
        example=6,
996
        validate=Range(min=1, error="Value must be greater than 0"),
997
    )
998
    parent_id = marshmallow.fields.Int(
999
        example=34,
1000
        validate=Range(min=0, error="Value must be positive or 0"),
1001
    )
1002
    raw_content = marshmallow.fields.String(
1003
        example='<p>This is just an html comment !</p>'
1004
    )
1005
    author = marshmallow.fields.Nested(UserDigestSchema)
1006
    created = marshmallow.fields.DateTime(
1007
        format=DATETIME_FORMAT,
1008
        description='comment creation date',
1009
    )
1010
1011
1012
class SetCommentSchema(marshmallow.Schema):
1013
    raw_content = marshmallow.fields.String(
1014
        example='<p>This is just an html comment !</p>',
1015
        validate=Length(min=1)
1016
        required=True,
1017
    )
1018
1019
    @post_load()
1020
    def create_comment(self, data):
1021
        return CommentCreation(**data)
1022
1023
1024
class ContentModifyAbstractSchema(marshmallow.Schema):
1025
    label = marshmallow.fields.String(
1026
        required=True,
1027
        example='contract for client XXX',
1028
        description='New title of the content',
1029
        validate=Length(min=1)
1030
    )
1031
1032
1033
class TextBasedContentModifySchema(ContentModifyAbstractSchema, TextBasedDataAbstractSchema):  # nopep8
1034
1035
    @post_load
1036
    def text_based_content_update(self, data):
1037
        return TextBasedContentUpdate(**data)
1038
1039
1040
class FolderContentModifySchema(ContentModifyAbstractSchema, TextBasedDataAbstractSchema):  # nopep
1041
    sub_content_types = marshmallow.fields.List(
1042
        marshmallow.fields.String(
1043
            example='html-document',
1044
            validate=all_content_types_validator,
1045
        ),
1046
        description='list of content types allowed as sub contents. '
1047
                    'This field is required for folder contents, '
1048
                    'set it to empty list in other cases'
1049
    )
1050
1051
    @post_load
1052
    def folder_content_update(self, data):
1053
        return FolderContentUpdate(**data)
1054
1055
1056
class FileContentModifySchema(TextBasedContentModifySchema):
1057
    pass
1058
1059
1060
class SetContentStatusSchema(marshmallow.Schema):
1061
    status = marshmallow.fields.Str(
1062
        example='closed-deprecated',
1063
        validate=OneOf(CONTENT_STATUS.get_all_slugs_values()),
1064
        description='this slug is found in content_type available statuses',
1065
        default=open_status,
1066
        required=True,
1067
    )
1068
1069
    @post_load
1070
    def set_status(self, data):
1071
        return SetContentStatus(**data)
1072