schemas.py 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. # coding=utf-8
  2. import marshmallow
  3. from marshmallow import post_load
  4. from marshmallow.validate import OneOf
  5. from tracim.models.auth import Profile
  6. from tracim.models.contents import CONTENT_DEFAULT_TYPE
  7. from tracim.models.contents import CONTENT_DEFAULT_STATUS
  8. from tracim.models.contents import GlobalStatus
  9. from tracim.models.contents import open_status
  10. from tracim.models.context_models import ContentCreation
  11. from tracim.models.context_models import MoveParams
  12. from tracim.models.context_models import WorkspaceAndContentPath
  13. from tracim.models.context_models import ContentFilter
  14. from tracim.models.context_models import LoginCredentials
  15. from tracim.models.data import UserRoleInWorkspace
  16. class UserDigestSchema(marshmallow.Schema):
  17. """
  18. Simple user schema
  19. """
  20. user_id = marshmallow.fields.Int(dump_only=True, example=3)
  21. avatar_url = marshmallow.fields.Url(
  22. allow_none=True,
  23. example="/api/v2/assets/avatars/suri-cate.jpg",
  24. description="avatar_url is the url to the image file. "
  25. "If no avatar, then set it to null "
  26. "(and frontend will interpret this with a default avatar)",
  27. )
  28. public_name = marshmallow.fields.String(
  29. example='Suri Cate',
  30. )
  31. class UserSchema(UserDigestSchema):
  32. """
  33. Complete user schema
  34. """
  35. email = marshmallow.fields.Email(
  36. required=True,
  37. example='suri.cate@algoo.fr'
  38. )
  39. created = marshmallow.fields.DateTime(
  40. format='%Y-%m-%dT%H:%M:%SZ',
  41. description='User account creation date',
  42. )
  43. is_active = marshmallow.fields.Bool(
  44. example=True,
  45. # TODO - G.M - Explains this value.
  46. )
  47. # TODO - G.M - 17-04-2018 - Restrict timezone values
  48. timezone = marshmallow.fields.String(
  49. example="Paris/Europe",
  50. )
  51. # TODO - G.M - 17-04-2018 - check this, relative url allowed ?
  52. caldav_url = marshmallow.fields.Url(
  53. allow_none=True,
  54. relative=True,
  55. attribute='calendar_url',
  56. example="/api/v2/calendar/user/3.ics/",
  57. description="The url for calendar CalDAV direct access",
  58. )
  59. profile = marshmallow.fields.String(
  60. attribute='profile',
  61. validate=OneOf(Profile._NAME),
  62. example='managers',
  63. )
  64. class Meta:
  65. description = 'User account of Tracim'
  66. # Path Schemas
  67. class UserIdPathSchema(marshmallow.Schema):
  68. user_id = marshmallow.fields.Int(example=3, required=True)
  69. class WorkspaceIdPathSchema(marshmallow.Schema):
  70. workspace_id = marshmallow.fields.Int(example=4, required=True)
  71. class ContentIdPathSchema(marshmallow.Schema):
  72. content_id = marshmallow.fields.Int(example=6, required=True)
  73. class WorkspaceAndContentIdPathSchema(
  74. WorkspaceIdPathSchema,
  75. ContentIdPathSchema
  76. ):
  77. @post_load
  78. def make_path_object(self, data):
  79. return WorkspaceAndContentPath(**data)
  80. class CommentsPathSchema(WorkspaceAndContentIdPathSchema):
  81. comment_id = marshmallow.fields.Int(
  82. example=6,
  83. description='id of a comment related to content content_id',
  84. required=True
  85. )
  86. class FilterContentQuerySchema(marshmallow.Schema):
  87. parent_id = marshmallow.fields.Int(
  88. example=2,
  89. default=0,
  90. description='allow to filter items in a folder.'
  91. ' If not set, then return all contents.'
  92. ' If set to 0, then return root contents.'
  93. ' If set to another value, return all contents'
  94. ' directly included in the folder parent_id'
  95. )
  96. show_archived = marshmallow.fields.Int(
  97. example=0,
  98. default=0,
  99. description='if set to 1, then show archived contents.'
  100. ' Default is 0 - hide archived content'
  101. )
  102. show_deleted = marshmallow.fields.Int(
  103. example=0,
  104. default=0,
  105. description='if set to 1, then show deleted contents.'
  106. ' Default is 0 - hide deleted content'
  107. )
  108. show_active = marshmallow.fields.Int(
  109. example=1,
  110. default=1,
  111. description='f set to 1, then show active contents. '
  112. 'Default is 1 - show active content.'
  113. ' Note: active content are content '
  114. 'that is neither archived nor deleted. '
  115. 'The reason for this parameter to exist is for example '
  116. 'to allow to show only archived documents'
  117. )
  118. @post_load
  119. def make_content_filter(self, data):
  120. return ContentFilter(**data)
  121. ###
  122. class BasicAuthSchema(marshmallow.Schema):
  123. email = marshmallow.fields.Email(
  124. example='suri.cate@algoo.fr',
  125. required=True
  126. )
  127. password = marshmallow.fields.String(
  128. example='8QLa$<w',
  129. required=True,
  130. load_only=True,
  131. )
  132. class Meta:
  133. description = 'Entry for HTTP Basic Auth'
  134. @post_load
  135. def make_login(self, data):
  136. return LoginCredentials(**data)
  137. class LoginOutputHeaders(marshmallow.Schema):
  138. expire_after = marshmallow.fields.String()
  139. class NoContentSchema(marshmallow.Schema):
  140. class Meta:
  141. description = 'Empty Schema'
  142. pass
  143. class WorkspaceMenuEntrySchema(marshmallow.Schema):
  144. slug = marshmallow.fields.String(example='markdown-pages')
  145. label = marshmallow.fields.String(example='Markdown Documents')
  146. route = marshmallow.fields.String(
  147. example='/#/workspace/{workspace_id}/contents/?type=mardown-page',
  148. description='the route is the frontend route. '
  149. 'It may include workspace_id '
  150. 'which must be replaced on backend size '
  151. '(the route must be ready-to-use)'
  152. )
  153. fa_icon = marshmallow.fields.String(
  154. example='file-text-o',
  155. description='CSS class of the icon. Example: file-o for using Fontawesome file-text-o icon', # nopep8
  156. )
  157. hexcolor = marshmallow.fields.String(
  158. example='#F0F9DC',
  159. description='Hexadecimal color of the entry.'
  160. )
  161. class Meta:
  162. description = 'Entry element of a workspace menu'
  163. class WorkspaceDigestSchema(marshmallow.Schema):
  164. workspace_id = marshmallow.fields.Int(example=4)
  165. slug = marshmallow.fields.String(example='intranet')
  166. label = marshmallow.fields.String(example='Intranet')
  167. sidebar_entries = marshmallow.fields.Nested(
  168. WorkspaceMenuEntrySchema,
  169. many=True,
  170. )
  171. class Meta:
  172. description = 'Digest of workspace informations'
  173. class WorkspaceSchema(WorkspaceDigestSchema):
  174. description = marshmallow.fields.String(example='All intranet data.')
  175. class Meta:
  176. description = 'Full workspace informations'
  177. class WorkspaceMemberSchema(marshmallow.Schema):
  178. role = marshmallow.fields.String(
  179. example='contributor',
  180. validate=OneOf(UserRoleInWorkspace.get_all_role_slug())
  181. )
  182. user_id = marshmallow.fields.Int(example=3)
  183. workspace_id = marshmallow.fields.Int(example=4)
  184. user = marshmallow.fields.Nested(
  185. UserSchema(only=('public_name', 'avatar_url'))
  186. )
  187. class Meta:
  188. description = 'Workspace Member information'
  189. class ApplicationConfigSchema(marshmallow.Schema):
  190. pass
  191. # TODO - G.M - 24-05-2018 - Set this
  192. class ApplicationSchema(marshmallow.Schema):
  193. label = marshmallow.fields.String(example='Calendar')
  194. slug = marshmallow.fields.String(example='calendar')
  195. fa_icon = marshmallow.fields.String(
  196. example='file-o',
  197. description='CSS class of the icon. Example: file-o for using Fontawesome file-o icon', # nopep8
  198. )
  199. hexcolor = marshmallow.fields.String(
  200. example='#FF0000',
  201. description='HTML encoded color associated to the application. Example:#FF0000 for red' # nopep8
  202. )
  203. is_active = marshmallow.fields.Boolean(
  204. example=True,
  205. description='if true, the application is in use in the context',
  206. )
  207. config = marshmallow.fields.Nested(
  208. ApplicationConfigSchema,
  209. )
  210. class Meta:
  211. description = 'Tracim Application informations'
  212. class StatusSchema(marshmallow.Schema):
  213. slug = marshmallow.fields.String(
  214. example='open',
  215. description='the slug represents the type of status. '
  216. 'Statuses are open, closed-validated, closed-invalidated, closed-deprecated' # nopep8
  217. )
  218. global_status = marshmallow.fields.String(
  219. example='open',
  220. description='global_status: open, closed',
  221. validate=OneOf([status.value for status in GlobalStatus]),
  222. )
  223. label = marshmallow.fields.String(example='Open')
  224. fa_icon = marshmallow.fields.String(example='fa-check')
  225. hexcolor = marshmallow.fields.String(example='#0000FF')
  226. class ContentTypeSchema(marshmallow.Schema):
  227. slug = marshmallow.fields.String(
  228. example='pagehtml',
  229. validate=OneOf([content.slug for content in CONTENT_DEFAULT_TYPE]),
  230. )
  231. fa_icon = marshmallow.fields.String(
  232. example='fa-file-text-o',
  233. description='CSS class of the icon. Example: file-o for using Fontawesome file-o icon', # nopep8
  234. )
  235. hexcolor = marshmallow.fields.String(
  236. example="#FF0000",
  237. description='HTML encoded color associated to the application. Example:#FF0000 for red' # nopep8
  238. )
  239. label = marshmallow.fields.String(
  240. example='Text Documents'
  241. )
  242. creation_label = marshmallow.fields.String(
  243. example='Write a document'
  244. )
  245. available_statuses = marshmallow.fields.Nested(
  246. StatusSchema,
  247. many=True
  248. )
  249. class ContentMoveSchema(marshmallow.Schema):
  250. # TODO - G.M - 30-05-2018 - Read and apply this note
  251. # Note:
  252. # if the new workspace is different, then the backend
  253. # must check if the user is allowed to move to this workspace
  254. # (the user must be content manager of both workspaces)
  255. new_parent_id = marshmallow.fields.Int(
  256. example=42,
  257. description='id of the new parent content id.',
  258. allow_none=True,
  259. required=True,
  260. )
  261. new_workspace_id = marshmallow.fields.Int(
  262. example=2,
  263. description='id of the new workspace id.',
  264. required=True
  265. )
  266. @post_load
  267. def make_move_params(self, data):
  268. return MoveParams(**data)
  269. class ContentCreationSchema(marshmallow.Schema):
  270. label = marshmallow.fields.String(
  271. example='contract for client XXX',
  272. description='Title of the content to create'
  273. )
  274. content_type = marshmallow.fields.String(
  275. example='htmlpage',
  276. validate=OneOf([content.slug for content in CONTENT_DEFAULT_TYPE]),
  277. )
  278. @post_load
  279. def make_content_filter(self, data):
  280. return ContentCreation(**data)
  281. class ContentDigestSchema(marshmallow.Schema):
  282. content_id = marshmallow.fields.Int(example=6)
  283. slug = marshmallow.fields.Str(example='intervention-report-12')
  284. parent_id = marshmallow.fields.Int(
  285. example=34,
  286. allow_none=True,
  287. default=None
  288. )
  289. workspace_id = marshmallow.fields.Int(
  290. example=19,
  291. )
  292. label = marshmallow.fields.Str(example='Intervention Report 12')
  293. content_type = marshmallow.fields.Str(
  294. example='htmlpage',
  295. validate=OneOf([content.slug for content in CONTENT_DEFAULT_TYPE]),
  296. )
  297. sub_content_types = marshmallow.fields.List(
  298. marshmallow.fields.String(),
  299. description='list of content types allowed as sub contents. '
  300. 'This field is required for folder contents, '
  301. 'set it to empty list in other cases'
  302. )
  303. status = marshmallow.fields.Str(
  304. example='closed-deprecated',
  305. validate=OneOf([status.slug for status in CONTENT_DEFAULT_STATUS]),
  306. description='this slug is found in content_type available statuses',
  307. default=open_status
  308. )
  309. is_archived = marshmallow.fields.Bool(example=False, default=False)
  310. is_deleted = marshmallow.fields.Bool(example=False, default=False)
  311. show_in_ui = marshmallow.fields.Bool(
  312. example=True,
  313. description='if false, then do not show content in the treeview. '
  314. 'This may his maybe used for specific contents or '
  315. 'for sub-contents. Default is True. '
  316. 'In first version of the API, this field is always True',
  317. )
  318. #####
  319. # Content
  320. #####
  321. class ContentSchema(ContentDigestSchema):
  322. current_revision_id = marshmallow.fields.Int(example=12)
  323. created = marshmallow.fields.DateTime(
  324. format='%Y-%m-%dT%H:%M:%SZ',
  325. description='Content creation date',
  326. )
  327. author = marshmallow.fields.Nested(UserDigestSchema)
  328. modified = marshmallow.fields.DateTime(
  329. format='%Y-%m-%dT%H:%M:%SZ',
  330. description='date of last modification of content',
  331. )
  332. last_modifier = marshmallow.fields.Nested(UserDigestSchema)
  333. class ThreadContentSchema(ContentSchema):
  334. raw_content = marshmallow.fields.String('Description of Thread')
  335. class HtmlDocumentContentSchema(ContentSchema):
  336. raw_content = marshmallow.fields.String('<p>Html page Content !</p>')
  337. #####
  338. # Revision
  339. #####
  340. class RevisionSchema(ContentDigestSchema):
  341. comments_id = marshmallow.fields.List(marshmallow.fields.Int(example=4))
  342. revision_id = marshmallow.fields.Int(example=12)
  343. created = marshmallow.fields.DateTime(
  344. format='%Y-%m-%dT%H:%M:%SZ',
  345. description='Content creation date',
  346. )
  347. author = marshmallow.fields.Nested(UserDigestSchema)
  348. class ThreadRevisionSchema(RevisionSchema):
  349. raw_content = marshmallow.fields.String('Description of Thread')
  350. class HtmlDocumentRevisionSchema(RevisionSchema):
  351. raw_content = marshmallow.fields.String('<p>Html page Content !</p>')
  352. ####
  353. class CommentSchema(marshmallow.Schema):
  354. content_id = marshmallow.fields.Int(example=6)
  355. parent_id = marshmallow.fields.Int(example=34)
  356. raw_content = marshmallow.fields.String(
  357. example='<p>This is just an html comment !</p>'
  358. )
  359. author = marshmallow.fields.Nested(UserDigestSchema)
  360. class ContentModifySchema(marshmallow.Schema):
  361. label = marshmallow.fields.String(
  362. example='contract for client XXX',
  363. description='New title of the content'
  364. )
  365. class HtmlDocumentModifySchema(ContentModifySchema):
  366. raw_content = marshmallow.fields.String('<p>Html page Content !</p>')
  367. class ThreadModifySchema(ContentModifySchema):
  368. raw_content = marshmallow.fields.String('Description of Thread')
  369. class SetCommentSchema(marshmallow.Schema):
  370. raw_content = marshmallow.fields.String(
  371. example='<p>This is just an html comment !</p>'
  372. )
  373. class SetContentStatusSchema(marshmallow.Schema):
  374. status = marshmallow.fields.Str(
  375. example='closed-deprecated',
  376. validate=OneOf([status.slug for status in CONTENT_DEFAULT_STATUS]),
  377. description='this slug is found in content_type available statuses',
  378. default=open_status
  379. )