schemas.py 15KB

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