schemas.py 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. # coding=utf-8
  2. import marshmallow
  3. from marshmallow import post_load
  4. from marshmallow.validate import OneOf
  5. from marshmallow.validate import Length
  6. from marshmallow.validate import Range
  7. from tracim_backend.lib.utils.utils import DATETIME_FORMAT
  8. from tracim_backend.models.auth import Profile
  9. from tracim_backend.app_models.contents import GlobalStatus
  10. from tracim_backend.app_models.contents import CONTENT_STATUS
  11. from tracim_backend.app_models.contents import CONTENT_TYPES
  12. from tracim_backend.app_models.contents import open_status
  13. from tracim_backend.models.auth import Group
  14. from tracim_backend.models.context_models import ActiveContentFilter
  15. from tracim_backend.models.context_models import FolderContentUpdate
  16. from tracim_backend.models.context_models import AutocompleteQuery
  17. from tracim_backend.models.context_models import ContentIdsQuery
  18. from tracim_backend.models.context_models import UserWorkspaceAndContentPath
  19. from tracim_backend.models.context_models import ContentCreation
  20. from tracim_backend.models.context_models import UserCreation
  21. from tracim_backend.models.context_models import SetEmail
  22. from tracim_backend.models.context_models import SetPassword
  23. from tracim_backend.models.context_models import UserInfos
  24. from tracim_backend.models.context_models import UserProfile
  25. from tracim_backend.models.context_models import ContentPreviewSizedPath
  26. from tracim_backend.models.context_models import RevisionPreviewSizedPath
  27. from tracim_backend.models.context_models import PageQuery
  28. from tracim_backend.models.context_models import WorkspaceAndContentRevisionPath
  29. from tracim_backend.models.context_models import WorkspaceMemberInvitation
  30. from tracim_backend.models.context_models import WorkspaceUpdate
  31. from tracim_backend.models.context_models import RoleUpdate
  32. from tracim_backend.models.context_models import CommentCreation
  33. from tracim_backend.models.context_models import TextBasedContentUpdate
  34. from tracim_backend.models.context_models import SetContentStatus
  35. from tracim_backend.models.context_models import CommentPath
  36. from tracim_backend.models.context_models import MoveParams
  37. from tracim_backend.models.context_models import WorkspaceAndContentPath
  38. from tracim_backend.models.context_models import WorkspaceAndUserPath
  39. from tracim_backend.models.context_models import ContentFilter
  40. from tracim_backend.models.context_models import LoginCredentials
  41. from tracim_backend.models.data import UserRoleInWorkspace
  42. from tracim_backend.models.data import ActionDescription
  43. from tracim_backend.app_models.validator import all_content_types_validator
  44. class UserDigestSchema(marshmallow.Schema):
  45. """
  46. Simple user schema
  47. """
  48. user_id = marshmallow.fields.Int(dump_only=True, example=3)
  49. avatar_url = marshmallow.fields.Url(
  50. allow_none=True,
  51. example="/api/v2/asset/avatars/suri-cate.jpg",
  52. description="avatar_url is the url to the image file. "
  53. "If no avatar, then set it to null "
  54. "(and frontend will interpret this with a default avatar)",
  55. )
  56. public_name = marshmallow.fields.String(
  57. example='Suri Cate',
  58. )
  59. class UserSchema(UserDigestSchema):
  60. """
  61. Complete user schema
  62. """
  63. email = marshmallow.fields.Email(
  64. required=True,
  65. example='suri.cate@algoo.fr'
  66. )
  67. created = marshmallow.fields.DateTime(
  68. format=DATETIME_FORMAT,
  69. description='User account creation date',
  70. )
  71. is_active = marshmallow.fields.Bool(
  72. example=True,
  73. description='Is user account activated ?'
  74. )
  75. is_deleted = marshmallow.fields.Bool(
  76. example=False,
  77. description='Is user account deleted ?'
  78. )
  79. # TODO - G.M - 17-04-2018 - Restrict timezone values
  80. timezone = marshmallow.fields.String(
  81. description="Timezone as tz database format",
  82. example="Europe/Paris",
  83. )
  84. # TODO - G.M - 17-04-2018 - check this, relative url allowed ?
  85. caldav_url = marshmallow.fields.Url(
  86. allow_none=True,
  87. relative=True,
  88. attribute='calendar_url',
  89. example="/api/v2/calendar/user/3.ics/",
  90. description="The url for calendar CalDAV direct access",
  91. )
  92. profile = marshmallow.fields.String(
  93. attribute='profile',
  94. validate=OneOf(Profile._NAME),
  95. example='managers',
  96. )
  97. lang = marshmallow.fields.String(
  98. description="User langage in iso639 format",
  99. example='en',
  100. required=False,
  101. validate=Length(min=2, max=3),
  102. allow_none=True,
  103. default=None,
  104. )
  105. class Meta:
  106. description = 'User account of Tracim'
  107. class LoggedInUserPasswordSchema(marshmallow.Schema):
  108. loggedin_user_password = marshmallow.fields.String(
  109. required=True,
  110. )
  111. class SetEmailSchema(LoggedInUserPasswordSchema):
  112. email = marshmallow.fields.Email(
  113. required=True,
  114. example='suri.cate@algoo.fr'
  115. )
  116. @post_load
  117. def create_set_email_object(self, data):
  118. return SetEmail(**data)
  119. class SetPasswordSchema(LoggedInUserPasswordSchema):
  120. new_password = marshmallow.fields.String(
  121. example='8QLa$<w',
  122. required=True
  123. )
  124. new_password2 = marshmallow.fields.String(
  125. example='8QLa$<w',
  126. required=True
  127. )
  128. @post_load
  129. def create_set_password_object(self, data):
  130. return SetPassword(**data)
  131. class UserInfosSchema(marshmallow.Schema):
  132. timezone = marshmallow.fields.String(
  133. description="Timezone as tz database format",
  134. example="Europe/Paris",
  135. required=True,
  136. )
  137. public_name = marshmallow.fields.String(
  138. example='Suri Cate',
  139. required=True,
  140. )
  141. lang = marshmallow.fields.String(
  142. description="User langage in iso639 format",
  143. example='en',
  144. required=True,
  145. validate=Length(min=2, max=3),
  146. allow_none=True,
  147. default=None,
  148. )
  149. @post_load
  150. def create_user_info_object(self, data):
  151. return UserInfos(**data)
  152. class UserProfileSchema(marshmallow.Schema):
  153. profile = marshmallow.fields.String(
  154. attribute='profile',
  155. validate=OneOf(Profile._NAME),
  156. example='managers',
  157. )
  158. @post_load
  159. def create_user_profile(self, data):
  160. return UserProfile(**data)
  161. class UserCreationSchema(marshmallow.Schema):
  162. email = marshmallow.fields.Email(
  163. required=True,
  164. example='suri.cate@algoo.fr'
  165. )
  166. password = marshmallow.fields.String(
  167. example='8QLa$<w',
  168. required=False,
  169. )
  170. profile = marshmallow.fields.String(
  171. attribute='profile',
  172. validate=OneOf(Profile._NAME),
  173. example='managers',
  174. required=False,
  175. default=Group.TIM_USER_GROUPNAME
  176. )
  177. timezone = marshmallow.fields.String(
  178. description="Timezone as tz database format",
  179. example="Europe/Paris",
  180. required=False,
  181. default=''
  182. )
  183. public_name = marshmallow.fields.String(
  184. example='Suri Cate',
  185. required=False,
  186. default=None,
  187. )
  188. lang = marshmallow.fields.String(
  189. description="User langage in iso639 format",
  190. example='en',
  191. required=False,
  192. validate=Length(min=2, max=3),
  193. allow_none=True,
  194. default=None,
  195. )
  196. email_notification = marshmallow.fields.Bool(
  197. example=True,
  198. required=False,
  199. default=True,
  200. )
  201. @post_load
  202. def create_user(self, data):
  203. return UserCreation(**data)
  204. # Path Schemas
  205. class UserIdPathSchema(marshmallow.Schema):
  206. user_id = marshmallow.fields.Int(
  207. example=3,
  208. required=True,
  209. description='id of a valid user',
  210. validate=Range(min=1, error="Value must be greater than 0"),
  211. )
  212. class WorkspaceIdPathSchema(marshmallow.Schema):
  213. workspace_id = marshmallow.fields.Int(
  214. example=4,
  215. required=True,
  216. description='id of a valid workspace',
  217. validate=Range(min=1, error="Value must be greater than 0"),
  218. )
  219. class ContentIdPathSchema(marshmallow.Schema):
  220. content_id = marshmallow.fields.Int(
  221. example=6,
  222. required=True,
  223. description='id of a valid content',
  224. validate=Range(min=1, error="Value must be greater than 0"),
  225. )
  226. class RevisionIdPathSchema(marshmallow.Schema):
  227. revision_id = marshmallow.fields.Int(example=6, required=True)
  228. class WorkspaceAndUserIdPathSchema(
  229. UserIdPathSchema,
  230. WorkspaceIdPathSchema
  231. ):
  232. @post_load
  233. def make_path_object(self, data):
  234. return WorkspaceAndUserPath(**data)
  235. class WorkspaceAndContentIdPathSchema(
  236. WorkspaceIdPathSchema,
  237. ContentIdPathSchema
  238. ):
  239. @post_load
  240. def make_path_object(self, data):
  241. return WorkspaceAndContentPath(**data)
  242. class WidthAndHeightPathSchema(marshmallow.Schema):
  243. width = marshmallow.fields.Int(example=256)
  244. height = marshmallow.fields.Int(example=256)
  245. class AllowedJpgPreviewSizesSchema(marshmallow.Schema):
  246. width = marshmallow.fields.Int(example=256)
  247. height = marshmallow.fields.Int(example=256)
  248. class AllowedJpgPreviewDimSchema(marshmallow.Schema):
  249. restricted = marshmallow.fields.Bool()
  250. dimensions = marshmallow.fields.Nested(
  251. AllowedJpgPreviewSizesSchema,
  252. many=True
  253. )
  254. class WorkspaceAndContentRevisionIdPathSchema(
  255. WorkspaceIdPathSchema,
  256. ContentIdPathSchema,
  257. RevisionIdPathSchema,
  258. ):
  259. @post_load
  260. def make_path_object(self, data):
  261. return WorkspaceAndContentRevisionPath(**data)
  262. class ContentPreviewSizedPathSchema(
  263. WorkspaceAndContentIdPathSchema,
  264. WidthAndHeightPathSchema
  265. ):
  266. @post_load
  267. def make_path_object(self, data):
  268. return ContentPreviewSizedPath(**data)
  269. class RevisionPreviewSizedPathSchema(
  270. WorkspaceAndContentRevisionIdPathSchema,
  271. WidthAndHeightPathSchema
  272. ):
  273. @post_load
  274. def make_path_object(self, data):
  275. return RevisionPreviewSizedPath(**data)
  276. class UserWorkspaceAndContentIdPathSchema(
  277. UserIdPathSchema,
  278. WorkspaceIdPathSchema,
  279. ContentIdPathSchema,
  280. ):
  281. @post_load
  282. def make_path_object(self, data):
  283. return UserWorkspaceAndContentPath(**data)
  284. class UserWorkspaceIdPathSchema(
  285. UserIdPathSchema,
  286. WorkspaceIdPathSchema,
  287. ):
  288. @post_load
  289. def make_path_object(self, data):
  290. return WorkspaceAndUserPath(**data)
  291. class CommentsPathSchema(WorkspaceAndContentIdPathSchema):
  292. comment_id = marshmallow.fields.Int(
  293. example=6,
  294. description='id of a valid comment related to content content_id',
  295. required=True,
  296. validate=Range(min=1, error="Value must be greater than 0"),
  297. )
  298. @post_load
  299. def make_path_object(self, data):
  300. return CommentPath(**data)
  301. class AutocompleteQuerySchema(marshmallow.Schema):
  302. acp = marshmallow.fields.Str(
  303. example='test',
  304. description='search text to query',
  305. validate=Length(min=2),
  306. required=True,
  307. )
  308. @post_load
  309. def make_autocomplete(self, data):
  310. return AutocompleteQuery(**data)
  311. class PageQuerySchema(marshmallow.Schema):
  312. page = marshmallow.fields.Int(
  313. example=2,
  314. default=0,
  315. description='allow to show a specific page of a pdf file',
  316. validate=Range(min=0, error="Value must be positive or 0"),
  317. )
  318. @post_load
  319. def make_page_query(self, data):
  320. return PageQuery(**data)
  321. class FilterContentQuerySchema(marshmallow.Schema):
  322. parent_id = marshmallow.fields.Int(
  323. example=2,
  324. default=0,
  325. description='allow to filter items in a folder.'
  326. ' If not set, then return all contents.'
  327. ' If set to 0, then return root contents.'
  328. ' If set to another value, return all contents'
  329. ' directly included in the folder parent_id',
  330. validate=Range(min=0, error="Value must be positive or 0"),
  331. )
  332. show_archived = marshmallow.fields.Int(
  333. example=0,
  334. default=0,
  335. description='if set to 1, then show archived contents.'
  336. ' Default is 0 - hide archived content',
  337. validate=Range(min=0, max=1, error="Value must be 0 or 1"),
  338. )
  339. show_deleted = marshmallow.fields.Int(
  340. example=0,
  341. default=0,
  342. description='if set to 1, then show deleted contents.'
  343. ' Default is 0 - hide deleted content',
  344. validate=Range(min=0, max=1, error="Value must be 0 or 1"),
  345. )
  346. show_active = marshmallow.fields.Int(
  347. example=1,
  348. default=1,
  349. description='f set to 1, then show active contents. '
  350. 'Default is 1 - show active content.'
  351. ' Note: active content are content '
  352. 'that is neither archived nor deleted. '
  353. 'The reason for this parameter to exist is for example '
  354. 'to allow to show only archived documents',
  355. validate=Range(min=0, max=1, error="Value must be 0 or 1"),
  356. )
  357. content_type = marshmallow.fields.String(
  358. example=CONTENT_TYPES.Any_SLUG,
  359. default=CONTENT_TYPES.Any_SLUG,
  360. validate=all_content_types_validator
  361. )
  362. @post_load
  363. def make_content_filter(self, data):
  364. return ContentFilter(**data)
  365. class ActiveContentFilterQuerySchema(marshmallow.Schema):
  366. limit = marshmallow.fields.Int(
  367. example=2,
  368. default=0,
  369. description='if 0 or not set, return all elements, else return only '
  370. 'the first limit elem (according to offset)',
  371. validate=Range(min=0, error="Value must be positive or 0"),
  372. )
  373. before_content_id = marshmallow.fields.Int(
  374. example=41,
  375. default=None,
  376. allow_none=True,
  377. description='return only content updated before this content',
  378. )
  379. @post_load
  380. def make_content_filter(self, data):
  381. return ActiveContentFilter(**data)
  382. class ContentIdsQuerySchema(marshmallow.Schema):
  383. contents_ids = marshmallow.fields.List(
  384. marshmallow.fields.Int(
  385. example=6,
  386. validate=Range(min=1, error="Value must be greater than 0"),
  387. )
  388. )
  389. @post_load
  390. def make_contents_ids(self, data):
  391. return ContentIdsQuery(**data)
  392. ###
  393. class RoleUpdateSchema(marshmallow.Schema):
  394. role = marshmallow.fields.String(
  395. example='contributor',
  396. validate=OneOf(UserRoleInWorkspace.get_all_role_slug())
  397. )
  398. @post_load
  399. def make_role(self, data):
  400. return RoleUpdate(**data)
  401. class WorkspaceMemberInviteSchema(RoleUpdateSchema):
  402. user_id = marshmallow.fields.Int(
  403. example=5,
  404. default=None,
  405. allow_none=True,
  406. )
  407. user_email_or_public_name = marshmallow.fields.String(
  408. example='suri@cate.fr',
  409. default=None,
  410. allow_none=True,
  411. )
  412. @post_load
  413. def make_role(self, data):
  414. return WorkspaceMemberInvitation(**data)
  415. class BasicAuthSchema(marshmallow.Schema):
  416. email = marshmallow.fields.Email(
  417. example='suri.cate@algoo.fr',
  418. required=True
  419. )
  420. password = marshmallow.fields.String(
  421. example='8QLa$<w',
  422. required=True,
  423. load_only=True,
  424. )
  425. class Meta:
  426. description = 'Entry for HTTP Basic Auth'
  427. @post_load
  428. def make_login(self, data):
  429. return LoginCredentials(**data)
  430. class LoginOutputHeaders(marshmallow.Schema):
  431. expire_after = marshmallow.fields.String()
  432. class WorkspaceModifySchema(marshmallow.Schema):
  433. label = marshmallow.fields.String(
  434. example='My Workspace',
  435. )
  436. description = marshmallow.fields.String(
  437. example='A super description of my workspace.',
  438. )
  439. @post_load
  440. def make_workspace_modifications(self, data):
  441. return WorkspaceUpdate(**data)
  442. class WorkspaceCreationSchema(WorkspaceModifySchema):
  443. pass
  444. class NoContentSchema(marshmallow.Schema):
  445. class Meta:
  446. description = 'Empty Schema'
  447. pass
  448. class WorkspaceMenuEntrySchema(marshmallow.Schema):
  449. slug = marshmallow.fields.String(example='markdown-pages')
  450. label = marshmallow.fields.String(example='Markdown Documents')
  451. route = marshmallow.fields.String(
  452. example='/#/workspace/{workspace_id}/contents/?type=mardown-page',
  453. description='the route is the frontend route. '
  454. 'It may include workspace_id '
  455. 'which must be replaced on backend size '
  456. '(the route must be ready-to-use)'
  457. )
  458. fa_icon = marshmallow.fields.String(
  459. example='file-text-o',
  460. description='CSS class of the icon. Example: file-o for using Fontawesome file-text-o icon', # nopep8
  461. )
  462. hexcolor = marshmallow.fields.String(
  463. example='#F0F9DC',
  464. description='Hexadecimal color of the entry.'
  465. )
  466. class Meta:
  467. description = 'Entry element of a workspace menu'
  468. class WorkspaceDigestSchema(marshmallow.Schema):
  469. workspace_id = marshmallow.fields.Int(
  470. example=4,
  471. validate=Range(min=1, error="Value must be greater than 0"),
  472. )
  473. slug = marshmallow.fields.String(example='intranet')
  474. label = marshmallow.fields.String(example='Intranet')
  475. sidebar_entries = marshmallow.fields.Nested(
  476. WorkspaceMenuEntrySchema,
  477. many=True,
  478. )
  479. is_deleted = marshmallow.fields.Bool(example=False, default=False)
  480. class Meta:
  481. description = 'Digest of workspace informations'
  482. class WorkspaceSchema(WorkspaceDigestSchema):
  483. description = marshmallow.fields.String(example='All intranet data.')
  484. class Meta:
  485. description = 'Full workspace informations'
  486. class WorkspaceMemberSchema(marshmallow.Schema):
  487. role = marshmallow.fields.String(
  488. example='contributor',
  489. validate=OneOf(UserRoleInWorkspace.get_all_role_slug())
  490. )
  491. user_id = marshmallow.fields.Int(
  492. example=3,
  493. validate=Range(min=1, error="Value must be greater than 0"),
  494. )
  495. workspace_id = marshmallow.fields.Int(
  496. example=4,
  497. validate=Range(min=1, error="Value must be greater than 0"),
  498. )
  499. user = marshmallow.fields.Nested(
  500. UserDigestSchema()
  501. )
  502. workspace = marshmallow.fields.Nested(
  503. WorkspaceDigestSchema(exclude=('sidebar_entries',))
  504. )
  505. is_active = marshmallow.fields.Bool()
  506. do_notify = marshmallow.fields.Bool(
  507. description='has user enabled notification for this workspace',
  508. example=True,
  509. )
  510. class Meta:
  511. description = 'Workspace Member information'
  512. class WorkspaceMemberCreationSchema(WorkspaceMemberSchema):
  513. newly_created = marshmallow.fields.Bool(
  514. exemple=False,
  515. description='Is the user completely new '
  516. '(and account was just created) or not ?',
  517. )
  518. email_sent = marshmallow.fields.Bool(
  519. exemple=False,
  520. description='Has an email been sent to user to inform him about '
  521. 'this new workspace registration and eventually his account'
  522. 'creation'
  523. )
  524. class ApplicationConfigSchema(marshmallow.Schema):
  525. pass
  526. # TODO - G.M - 24-05-2018 - Set this
  527. class ApplicationSchema(marshmallow.Schema):
  528. label = marshmallow.fields.String(example='Calendar')
  529. slug = marshmallow.fields.String(example='calendar')
  530. fa_icon = marshmallow.fields.String(
  531. example='file-o',
  532. description='CSS class of the icon. Example: file-o for using Fontawesome file-o icon', # nopep8
  533. )
  534. hexcolor = marshmallow.fields.String(
  535. example='#FF0000',
  536. description='HTML encoded color associated to the application. Example:#FF0000 for red' # nopep8
  537. )
  538. is_active = marshmallow.fields.Boolean(
  539. example=True,
  540. description='if true, the application is in use in the context',
  541. )
  542. config = marshmallow.fields.Nested(
  543. ApplicationConfigSchema,
  544. )
  545. class Meta:
  546. description = 'Tracim Application informations'
  547. class StatusSchema(marshmallow.Schema):
  548. slug = marshmallow.fields.String(
  549. example='open',
  550. description='the slug represents the type of status. '
  551. 'Statuses are open, closed-validated, closed-invalidated, closed-deprecated' # nopep8
  552. )
  553. global_status = marshmallow.fields.String(
  554. example='open',
  555. description='global_status: open, closed',
  556. validate=OneOf([status.value for status in GlobalStatus]),
  557. )
  558. label = marshmallow.fields.String(example='Open')
  559. fa_icon = marshmallow.fields.String(example='fa-check')
  560. hexcolor = marshmallow.fields.String(example='#0000FF')
  561. class ContentTypeSchema(marshmallow.Schema):
  562. slug = marshmallow.fields.String(
  563. example='pagehtml',
  564. validate=all_content_types_validator,
  565. )
  566. fa_icon = marshmallow.fields.String(
  567. example='fa-file-text-o',
  568. description='CSS class of the icon. Example: file-o for using Fontawesome file-o icon', # nopep8
  569. )
  570. hexcolor = marshmallow.fields.String(
  571. example="#FF0000",
  572. description='HTML encoded color associated to the application. Example:#FF0000 for red' # nopep8
  573. )
  574. label = marshmallow.fields.String(
  575. example='Text Documents'
  576. )
  577. creation_label = marshmallow.fields.String(
  578. example='Write a document'
  579. )
  580. available_statuses = marshmallow.fields.Nested(
  581. StatusSchema,
  582. many=True
  583. )
  584. class ContentMoveSchema(marshmallow.Schema):
  585. # TODO - G.M - 30-05-2018 - Read and apply this note
  586. # Note:
  587. # if the new workspace is different, then the backend
  588. # must check if the user is allowed to move to this workspace
  589. # (the user must be content manager of both workspaces)
  590. new_parent_id = marshmallow.fields.Int(
  591. example=42,
  592. description='id of the new parent content id.',
  593. allow_none=True,
  594. required=True,
  595. validate=Range(min=0, error="Value must be positive or 0"),
  596. )
  597. new_workspace_id = marshmallow.fields.Int(
  598. example=2,
  599. description='id of the new workspace id.',
  600. required=True,
  601. validate=Range(min=1, error="Value must be greater than 0"),
  602. )
  603. @post_load
  604. def make_move_params(self, data):
  605. return MoveParams(**data)
  606. class ContentCreationSchema(marshmallow.Schema):
  607. label = marshmallow.fields.String(
  608. example='contract for client XXX',
  609. description='Title of the content to create'
  610. )
  611. content_type = marshmallow.fields.String(
  612. example='html-document',
  613. validate=all_content_types_validator,
  614. )
  615. parent_id = marshmallow.fields.Integer(
  616. example=35,
  617. description='content_id of parent content, if content should be placed in a folder, this should be folder content_id.', # nopep8
  618. allow_none=True,
  619. default=None,
  620. validate=Range(min=1, error="Value must be positive"),
  621. )
  622. @post_load
  623. def make_content_creation(self, data):
  624. return ContentCreation(**data)
  625. class ContentDigestSchema(marshmallow.Schema):
  626. content_id = marshmallow.fields.Int(
  627. example=6,
  628. validate=Range(min=1, error="Value must be greater than 0"),
  629. )
  630. slug = marshmallow.fields.Str(example='intervention-report-12')
  631. parent_id = marshmallow.fields.Int(
  632. example=34,
  633. allow_none=True,
  634. default=None,
  635. validate=Range(min=0, error="Value must be positive or 0"),
  636. )
  637. workspace_id = marshmallow.fields.Int(
  638. example=19,
  639. validate=Range(min=1, error="Value must be greater than 0"),
  640. )
  641. label = marshmallow.fields.Str(example='Intervention Report 12')
  642. content_type = marshmallow.fields.Str(
  643. example='html-document',
  644. validate=all_content_types_validator,
  645. )
  646. sub_content_types = marshmallow.fields.List(
  647. marshmallow.fields.String(
  648. example='html-content',
  649. validate=all_content_types_validator
  650. ),
  651. description='list of content types allowed as sub contents. '
  652. 'This field is required for folder contents, '
  653. 'set it to empty list in other cases'
  654. )
  655. status = marshmallow.fields.Str(
  656. example='closed-deprecated',
  657. validate=OneOf(CONTENT_STATUS.get_all_slugs_values()),
  658. description='this slug is found in content_type available statuses',
  659. default=open_status
  660. )
  661. is_archived = marshmallow.fields.Bool(example=False, default=False)
  662. is_deleted = marshmallow.fields.Bool(example=False, default=False)
  663. show_in_ui = marshmallow.fields.Bool(
  664. example=True,
  665. description='if false, then do not show content in the treeview. '
  666. 'This may his maybe used for specific contents or '
  667. 'for sub-contents. Default is True. '
  668. 'In first version of the API, this field is always True',
  669. )
  670. class ReadStatusSchema(marshmallow.Schema):
  671. content_id = marshmallow.fields.Int(
  672. example=6,
  673. validate=Range(min=1, error="Value must be greater than 0"),
  674. )
  675. read_by_user = marshmallow.fields.Bool(example=False, default=False)
  676. #####
  677. # Content
  678. #####
  679. class ContentSchema(ContentDigestSchema):
  680. current_revision_id = marshmallow.fields.Int(example=12)
  681. created = marshmallow.fields.DateTime(
  682. format=DATETIME_FORMAT,
  683. description='Content creation date',
  684. )
  685. author = marshmallow.fields.Nested(UserDigestSchema)
  686. modified = marshmallow.fields.DateTime(
  687. format=DATETIME_FORMAT,
  688. description='date of last modification of content',
  689. )
  690. last_modifier = marshmallow.fields.Nested(UserDigestSchema)
  691. class TextBasedDataAbstractSchema(marshmallow.Schema):
  692. raw_content = marshmallow.fields.String(
  693. description='Content of the object, may be raw text or <b>html</b> for example' # nopep8
  694. )
  695. class FileInfoAbstractSchema(marshmallow.Schema):
  696. raw_content = marshmallow.fields.String(
  697. description='raw text or html description of the file'
  698. )
  699. class TextBasedContentSchema(ContentSchema, TextBasedDataAbstractSchema):
  700. pass
  701. class FileContentSchema(ContentSchema, FileInfoAbstractSchema):
  702. pass
  703. #####
  704. # Revision
  705. #####
  706. class RevisionSchema(ContentDigestSchema):
  707. comment_ids = marshmallow.fields.List(
  708. marshmallow.fields.Int(
  709. example=4,
  710. validate=Range(min=1, error="Value must be greater than 0"),
  711. )
  712. )
  713. revision_id = marshmallow.fields.Int(
  714. example=12,
  715. validate=Range(min=1, error="Value must be greater than 0"),
  716. )
  717. revision_type = marshmallow.fields.String(
  718. example=ActionDescription.CREATION,
  719. validate=OneOf(ActionDescription.allowed_values()),
  720. )
  721. created = marshmallow.fields.DateTime(
  722. format=DATETIME_FORMAT,
  723. description='Content creation date',
  724. )
  725. author = marshmallow.fields.Nested(UserDigestSchema)
  726. class TextBasedRevisionSchema(RevisionSchema, TextBasedDataAbstractSchema):
  727. pass
  728. class FileRevisionSchema(RevisionSchema, FileInfoAbstractSchema):
  729. pass
  730. class CommentSchema(marshmallow.Schema):
  731. content_id = marshmallow.fields.Int(
  732. example=6,
  733. validate=Range(min=1, error="Value must be greater than 0"),
  734. )
  735. parent_id = marshmallow.fields.Int(
  736. example=34,
  737. validate=Range(min=0, error="Value must be positive or 0"),
  738. )
  739. raw_content = marshmallow.fields.String(
  740. example='<p>This is just an html comment !</p>'
  741. )
  742. author = marshmallow.fields.Nested(UserDigestSchema)
  743. created = marshmallow.fields.DateTime(
  744. format=DATETIME_FORMAT,
  745. description='comment creation date',
  746. )
  747. class SetCommentSchema(marshmallow.Schema):
  748. raw_content = marshmallow.fields.String(
  749. example='<p>This is just an html comment !</p>'
  750. )
  751. @post_load()
  752. def create_comment(self, data):
  753. return CommentCreation(**data)
  754. class ContentModifyAbstractSchema(marshmallow.Schema):
  755. label = marshmallow.fields.String(
  756. required=True,
  757. example='contract for client XXX',
  758. description='New title of the content'
  759. )
  760. class TextBasedContentModifySchema(ContentModifyAbstractSchema, TextBasedDataAbstractSchema): # nopep8
  761. @post_load
  762. def text_based_content_update(self, data):
  763. return TextBasedContentUpdate(**data)
  764. class FolderContentModifySchema(ContentModifyAbstractSchema, TextBasedDataAbstractSchema): # nopep
  765. sub_content_types = marshmallow.fields.List(
  766. marshmallow.fields.String(
  767. example='html-document',
  768. validate=all_content_types_validator,
  769. ),
  770. description='list of content types allowed as sub contents. '
  771. 'This field is required for folder contents, '
  772. 'set it to empty list in other cases'
  773. )
  774. @post_load
  775. def folder_content_update(self, data):
  776. return FolderContentUpdate(**data)
  777. class FileContentModifySchema(TextBasedContentModifySchema):
  778. pass
  779. class SetContentStatusSchema(marshmallow.Schema):
  780. status = marshmallow.fields.Str(
  781. example='closed-deprecated',
  782. validate=OneOf(CONTENT_STATUS.get_all_slugs_values()),
  783. description='this slug is found in content_type available statuses',
  784. default=open_status,
  785. required=True,
  786. )
  787. @post_load
  788. def set_status(self, data):
  789. return SetContentStatus(**data)