You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

repo_form.go 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2017 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package auth
  6. import (
  7. "net/url"
  8. "strings"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/routers/utils"
  11. "github.com/Unknwon/com"
  12. "github.com/go-macaron/binding"
  13. "gopkg.in/macaron.v1"
  14. )
  15. // _______________________________________ _________.______________________ _______________.___.
  16. // \______ \_ _____/\______ \_____ \ / _____/| \__ ___/\_____ \\______ \__ | |
  17. // | _/| __)_ | ___// | \ \_____ \ | | | | / | \| _// | |
  18. // | | \| \ | | / | \/ \| | | | / | \ | \\____ |
  19. // |____|_ /_______ / |____| \_______ /_______ /|___| |____| \_______ /____|_ // ______|
  20. // \/ \/ \/ \/ \/ \/ \/
  21. // CreateRepoForm form for creating repository
  22. type CreateRepoForm struct {
  23. UID int64 `binding:"Required"`
  24. RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
  25. Private bool
  26. Description string `binding:"MaxSize(255)"`
  27. AutoInit bool
  28. Gitignores string
  29. License string
  30. Readme string
  31. }
  32. // Validate validates the fields
  33. func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  34. return validate(errs, ctx.Data, f, ctx.Locale)
  35. }
  36. // MigrateRepoForm form for migrating repository
  37. type MigrateRepoForm struct {
  38. // required: true
  39. CloneAddr string `json:"clone_addr" binding:"Required"`
  40. AuthUsername string `json:"auth_username"`
  41. AuthPassword string `json:"auth_password"`
  42. // required: true
  43. UID int64 `json:"uid" binding:"Required"`
  44. // required: true
  45. RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"`
  46. Mirror bool `json:"mirror"`
  47. Private bool `json:"private"`
  48. Description string `json:"description" binding:"MaxSize(255)"`
  49. }
  50. // Validate validates the fields
  51. func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  52. return validate(errs, ctx.Data, f, ctx.Locale)
  53. }
  54. // ParseRemoteAddr checks if given remote address is valid,
  55. // and returns composed URL with needed username and password.
  56. // It also checks if given user has permission when remote address
  57. // is actually a local path.
  58. func (f MigrateRepoForm) ParseRemoteAddr(user *models.User) (string, error) {
  59. remoteAddr := strings.TrimSpace(f.CloneAddr)
  60. // Remote address can be HTTP/HTTPS/Git URL or local path.
  61. if strings.HasPrefix(remoteAddr, "http://") ||
  62. strings.HasPrefix(remoteAddr, "https://") ||
  63. strings.HasPrefix(remoteAddr, "git://") {
  64. u, err := url.Parse(remoteAddr)
  65. if err != nil {
  66. return "", models.ErrInvalidCloneAddr{IsURLError: true}
  67. }
  68. if len(f.AuthUsername)+len(f.AuthPassword) > 0 {
  69. u.User = url.UserPassword(f.AuthUsername, f.AuthPassword)
  70. }
  71. remoteAddr = u.String()
  72. } else if !user.CanImportLocal() {
  73. return "", models.ErrInvalidCloneAddr{IsPermissionDenied: true}
  74. } else if !com.IsDir(remoteAddr) {
  75. return "", models.ErrInvalidCloneAddr{IsInvalidPath: true}
  76. }
  77. return remoteAddr, nil
  78. }
  79. // RepoSettingForm form for changing repository settings
  80. type RepoSettingForm struct {
  81. RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
  82. Description string `binding:"MaxSize(255)"`
  83. Website string `binding:"ValidUrl;MaxSize(255)"`
  84. Interval string
  85. MirrorAddress string
  86. Private bool
  87. EnablePrune bool
  88. // Advanced settings
  89. EnableWiki bool
  90. EnableExternalWiki bool
  91. ExternalWikiURL string
  92. EnableIssues bool
  93. EnableExternalTracker bool
  94. ExternalTrackerURL string
  95. TrackerURLFormat string
  96. TrackerIssueStyle string
  97. EnablePulls bool
  98. PullsIgnoreWhitespace bool
  99. PullsAllowMerge bool
  100. PullsAllowRebase bool
  101. PullsAllowSquash bool
  102. EnableTimetracker bool
  103. AllowOnlyContributorsToTrackTime bool
  104. EnableIssueDependencies bool
  105. // Admin settings
  106. EnableHealthCheck bool
  107. }
  108. // Validate validates the fields
  109. func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  110. return validate(errs, ctx.Data, f, ctx.Locale)
  111. }
  112. // __________ .__
  113. // \______ \____________ ____ ____ | |__
  114. // | | _/\_ __ \__ \ / \_/ ___\| | \
  115. // | | \ | | \// __ \| | \ \___| Y \
  116. // |______ / |__| (____ /___| /\___ >___| /
  117. // \/ \/ \/ \/ \/
  118. // ProtectBranchForm form for changing protected branch settings
  119. type ProtectBranchForm struct {
  120. Protected bool
  121. EnableWhitelist bool
  122. WhitelistUsers string
  123. WhitelistTeams string
  124. EnableMergeWhitelist bool
  125. MergeWhitelistUsers string
  126. MergeWhitelistTeams string
  127. }
  128. // Validate validates the fields
  129. func (f *ProtectBranchForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  130. return validate(errs, ctx.Data, f, ctx.Locale)
  131. }
  132. // __ __ ___. .__ .__ __
  133. // / \ / \ ____\_ |__ | |__ | |__ ____ | | __
  134. // \ \/\/ // __ \| __ \| | \| | \ / _ \| |/ /
  135. // \ /\ ___/| \_\ \ Y \ Y ( <_> ) <
  136. // \__/\ / \___ >___ /___| /___| /\____/|__|_ \
  137. // \/ \/ \/ \/ \/ \/
  138. // WebhookForm form for changing web hook
  139. type WebhookForm struct {
  140. Events string
  141. Create bool
  142. Delete bool
  143. Fork bool
  144. Issues bool
  145. IssueComment bool
  146. Release bool
  147. Push bool
  148. PullRequest bool
  149. Repository bool
  150. Active bool
  151. }
  152. // PushOnly if the hook will be triggered when push
  153. func (f WebhookForm) PushOnly() bool {
  154. return f.Events == "push_only"
  155. }
  156. // SendEverything if the hook will be triggered any event
  157. func (f WebhookForm) SendEverything() bool {
  158. return f.Events == "send_everything"
  159. }
  160. // ChooseEvents if the hook will be triggered choose events
  161. func (f WebhookForm) ChooseEvents() bool {
  162. return f.Events == "choose_events"
  163. }
  164. // NewWebhookForm form for creating web hook
  165. type NewWebhookForm struct {
  166. PayloadURL string `binding:"Required;ValidUrl"`
  167. ContentType int `binding:"Required"`
  168. Secret string
  169. WebhookForm
  170. }
  171. // Validate validates the fields
  172. func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  173. return validate(errs, ctx.Data, f, ctx.Locale)
  174. }
  175. // NewGogshookForm form for creating gogs hook
  176. type NewGogshookForm struct {
  177. PayloadURL string `binding:"Required;ValidUrl"`
  178. ContentType int `binding:"Required"`
  179. Secret string
  180. WebhookForm
  181. }
  182. // Validate validates the fields
  183. func (f *NewGogshookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  184. return validate(errs, ctx.Data, f, ctx.Locale)
  185. }
  186. // NewSlackHookForm form for creating slack hook
  187. type NewSlackHookForm struct {
  188. PayloadURL string `binding:"Required;ValidUrl"`
  189. Channel string `binding:"Required"`
  190. Username string
  191. IconURL string
  192. Color string
  193. WebhookForm
  194. }
  195. // Validate validates the fields
  196. func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  197. return validate(errs, ctx.Data, f, ctx.Locale)
  198. }
  199. // HasInvalidChannel validates the channel name is in the right format
  200. func (f NewSlackHookForm) HasInvalidChannel() bool {
  201. return !utils.IsValidSlackChannel(f.Channel)
  202. }
  203. // NewDiscordHookForm form for creating discord hook
  204. type NewDiscordHookForm struct {
  205. PayloadURL string `binding:"Required;ValidUrl"`
  206. Username string
  207. IconURL string
  208. WebhookForm
  209. }
  210. // Validate validates the fields
  211. func (f *NewDiscordHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  212. return validate(errs, ctx.Data, f, ctx.Locale)
  213. }
  214. // NewDingtalkHookForm form for creating dingtalk hook
  215. type NewDingtalkHookForm struct {
  216. PayloadURL string `binding:"Required;ValidUrl"`
  217. WebhookForm
  218. }
  219. // Validate validates the fields
  220. func (f *NewDingtalkHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  221. return validate(errs, ctx.Data, f, ctx.Locale)
  222. }
  223. // .___
  224. // | | ______ ________ __ ____
  225. // | |/ ___// ___/ | \_/ __ \
  226. // | |\___ \ \___ \| | /\ ___/
  227. // |___/____ >____ >____/ \___ >
  228. // \/ \/ \/
  229. // CreateIssueForm form for creating issue
  230. type CreateIssueForm struct {
  231. Title string `binding:"Required;MaxSize(255)"`
  232. LabelIDs string `form:"label_ids"`
  233. AssigneeIDs string `form:"assignee_ids"`
  234. Ref string `form:"ref"`
  235. MilestoneID int64
  236. AssigneeID int64
  237. Content string
  238. Files []string
  239. }
  240. // Validate validates the fields
  241. func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  242. return validate(errs, ctx.Data, f, ctx.Locale)
  243. }
  244. // CreateCommentForm form for creating comment
  245. type CreateCommentForm struct {
  246. Content string
  247. Status string `binding:"OmitEmpty;In(reopen,close)"`
  248. Files []string
  249. }
  250. // Validate validates the fields
  251. func (f *CreateCommentForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  252. return validate(errs, ctx.Data, f, ctx.Locale)
  253. }
  254. // ReactionForm form for adding and removing reaction
  255. type ReactionForm struct {
  256. Content string `binding:"Required;In(+1,-1,laugh,confused,heart,hooray)"`
  257. }
  258. // Validate validates the fields
  259. func (f *ReactionForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  260. return validate(errs, ctx.Data, f, ctx.Locale)
  261. }
  262. // _____ .__.__ __
  263. // / \ |__| | ____ _______/ |_ ____ ____ ____
  264. // / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \
  265. // / Y \ | |_\ ___/ \___ \ | | ( <_> ) | \ ___/
  266. // \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ >
  267. // \/ \/ \/ \/ \/
  268. // CreateMilestoneForm form for creating milestone
  269. type CreateMilestoneForm struct {
  270. Title string `binding:"Required;MaxSize(50)"`
  271. Content string
  272. Deadline string
  273. }
  274. // Validate validates the fields
  275. func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  276. return validate(errs, ctx.Data, f, ctx.Locale)
  277. }
  278. // .____ ___. .__
  279. // | | _____ \_ |__ ____ | |
  280. // | | \__ \ | __ \_/ __ \| |
  281. // | |___ / __ \| \_\ \ ___/| |__
  282. // |_______ (____ /___ /\___ >____/
  283. // \/ \/ \/ \/
  284. // CreateLabelForm form for creating label
  285. type CreateLabelForm struct {
  286. ID int64
  287. Title string `binding:"Required;MaxSize(50)" locale:"repo.issues.label_title"`
  288. Description string `binding:"MaxSize(200)" locale:"repo.issues.label_description"`
  289. Color string `binding:"Required;Size(7)" locale:"repo.issues.label_color"`
  290. }
  291. // Validate validates the fields
  292. func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  293. return validate(errs, ctx.Data, f, ctx.Locale)
  294. }
  295. // InitializeLabelsForm form for initializing labels
  296. type InitializeLabelsForm struct {
  297. TemplateName string `binding:"Required"`
  298. }
  299. // Validate validates the fields
  300. func (f *InitializeLabelsForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  301. return validate(errs, ctx.Data, f, ctx.Locale)
  302. }
  303. // __________ .__ .__ __________ __
  304. // \______ \__ __| | | | \______ \ ____ ________ __ ____ _______/ |_
  305. // | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\
  306. // | | | | / |_| |__ | | \ ___< <_| | | /\ ___/ \___ \ | |
  307. // |____| |____/|____/____/ |____|_ /\___ >__ |____/ \___ >____ > |__|
  308. // \/ \/ |__| \/ \/
  309. // MergePullRequestForm form for merging Pull Request
  310. type MergePullRequestForm struct {
  311. Do string `binding:"Required;In(merge,rebase,squash)"`
  312. MergeTitleField string
  313. MergeMessageField string
  314. }
  315. // Validate validates the fields
  316. func (f *MergePullRequestForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  317. return validate(errs, ctx.Data, f, ctx.Locale)
  318. }
  319. // CodeCommentForm form for adding code comments for PRs
  320. type CodeCommentForm struct {
  321. Content string `binding:"Required"`
  322. Side string `binding:"Required;In(previous,proposed)"`
  323. Line int64
  324. TreePath string `form:"path" binding:"Required"`
  325. IsReview bool `form:"is_review"`
  326. Reply int64 `form:"reply"`
  327. }
  328. // Validate validates the fields
  329. func (f *CodeCommentForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  330. return validate(errs, ctx.Data, f, ctx.Locale)
  331. }
  332. // SubmitReviewForm for submitting a finished code review
  333. type SubmitReviewForm struct {
  334. Content string
  335. Type string `binding:"Required;In(approve,comment,reject)"`
  336. }
  337. // Validate validates the fields
  338. func (f *SubmitReviewForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  339. return validate(errs, ctx.Data, f, ctx.Locale)
  340. }
  341. // ReviewType will return the corresponding reviewtype for type
  342. func (f SubmitReviewForm) ReviewType() models.ReviewType {
  343. switch f.Type {
  344. case "approve":
  345. return models.ReviewTypeApprove
  346. case "comment":
  347. return models.ReviewTypeComment
  348. case "reject":
  349. return models.ReviewTypeReject
  350. default:
  351. return models.ReviewTypeUnknown
  352. }
  353. }
  354. // HasEmptyContent checks if the content of the review form is empty.
  355. func (f SubmitReviewForm) HasEmptyContent() bool {
  356. reviewType := f.ReviewType()
  357. return (reviewType == models.ReviewTypeComment || reviewType == models.ReviewTypeReject) &&
  358. len(strings.TrimSpace(f.Content)) == 0
  359. }
  360. // __________ .__
  361. // \______ \ ____ | | ____ _____ ______ ____
  362. // | _// __ \| | _/ __ \\__ \ / ___// __ \
  363. // | | \ ___/| |_\ ___/ / __ \_\___ \\ ___/
  364. // |____|_ /\___ >____/\___ >____ /____ >\___ >
  365. // \/ \/ \/ \/ \/ \/
  366. // NewReleaseForm form for creating release
  367. type NewReleaseForm struct {
  368. TagName string `binding:"Required;GitRefName"`
  369. Target string `form:"tag_target" binding:"Required"`
  370. Title string `binding:"Required"`
  371. Content string
  372. Draft string
  373. Prerelease bool
  374. Files []string
  375. }
  376. // Validate validates the fields
  377. func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  378. return validate(errs, ctx.Data, f, ctx.Locale)
  379. }
  380. // EditReleaseForm form for changing release
  381. type EditReleaseForm struct {
  382. Title string `form:"title" binding:"Required"`
  383. Content string `form:"content"`
  384. Draft string `form:"draft"`
  385. Prerelease bool `form:"prerelease"`
  386. Files []string
  387. }
  388. // Validate validates the fields
  389. func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  390. return validate(errs, ctx.Data, f, ctx.Locale)
  391. }
  392. // __ __.__ __ .__
  393. // / \ / \__| | _|__|
  394. // \ \/\/ / | |/ / |
  395. // \ /| | <| |
  396. // \__/\ / |__|__|_ \__|
  397. // \/ \/
  398. // NewWikiForm form for creating wiki
  399. type NewWikiForm struct {
  400. Title string `binding:"Required"`
  401. Content string `binding:"Required"`
  402. Message string
  403. }
  404. // Validate validates the fields
  405. // FIXME: use code generation to generate this method.
  406. func (f *NewWikiForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  407. return validate(errs, ctx.Data, f, ctx.Locale)
  408. }
  409. // ___________ .___.__ __
  410. // \_ _____/ __| _/|__|/ |_
  411. // | __)_ / __ | | \ __\
  412. // | \/ /_/ | | || |
  413. // /_______ /\____ | |__||__|
  414. // \/ \/
  415. // EditRepoFileForm form for changing repository file
  416. type EditRepoFileForm struct {
  417. TreePath string `binding:"Required;MaxSize(500)"`
  418. Content string `binding:"Required"`
  419. CommitSummary string `binding:"MaxSize(100)"`
  420. CommitMessage string
  421. CommitChoice string `binding:"Required;MaxSize(50)"`
  422. NewBranchName string `binding:"GitRefName;MaxSize(100)"`
  423. LastCommit string
  424. }
  425. // Validate validates the fields
  426. func (f *EditRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  427. return validate(errs, ctx.Data, f, ctx.Locale)
  428. }
  429. // EditPreviewDiffForm form for changing preview diff
  430. type EditPreviewDiffForm struct {
  431. Content string
  432. }
  433. // Validate validates the fields
  434. func (f *EditPreviewDiffForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  435. return validate(errs, ctx.Data, f, ctx.Locale)
  436. }
  437. // ____ ___ .__ .___
  438. // | | \______ | | _________ __| _/
  439. // | | /\____ \| | / _ \__ \ / __ |
  440. // | | / | |_> > |_( <_> ) __ \_/ /_/ |
  441. // |______/ | __/|____/\____(____ /\____ |
  442. // |__| \/ \/
  443. //
  444. // UploadRepoFileForm form for uploading repository file
  445. type UploadRepoFileForm struct {
  446. TreePath string `binding:"MaxSize(500)"`
  447. CommitSummary string `binding:"MaxSize(100)"`
  448. CommitMessage string
  449. CommitChoice string `binding:"Required;MaxSize(50)"`
  450. NewBranchName string `binding:"GitRefName;MaxSize(100)"`
  451. Files []string
  452. }
  453. // Validate validates the fields
  454. func (f *UploadRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  455. return validate(errs, ctx.Data, f, ctx.Locale)
  456. }
  457. // RemoveUploadFileForm form for removing uploaded file
  458. type RemoveUploadFileForm struct {
  459. File string `binding:"Required;MaxSize(50)"`
  460. }
  461. // Validate validates the fields
  462. func (f *RemoveUploadFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  463. return validate(errs, ctx.Data, f, ctx.Locale)
  464. }
  465. // ________ .__ __
  466. // \______ \ ____ | | _____/ |_ ____
  467. // | | \_/ __ \| | _/ __ \ __\/ __ \
  468. // | ` \ ___/| |_\ ___/| | \ ___/
  469. // /_______ /\___ >____/\___ >__| \___ >
  470. // \/ \/ \/ \/
  471. // DeleteRepoFileForm form for deleting repository file
  472. type DeleteRepoFileForm struct {
  473. CommitSummary string `binding:"MaxSize(100)"`
  474. CommitMessage string
  475. CommitChoice string `binding:"Required;MaxSize(50)"`
  476. NewBranchName string `binding:"GitRefName;MaxSize(100)"`
  477. }
  478. // Validate validates the fields
  479. func (f *DeleteRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  480. return validate(errs, ctx.Data, f, ctx.Locale)
  481. }
  482. // ___________.__ ___________ __
  483. // \__ ___/|__| _____ ____ \__ ___/___________ ____ | | __ ___________
  484. // | | | |/ \_/ __ \ | | \_ __ \__ \ _/ ___\| |/ // __ \_ __ \
  485. // | | | | Y Y \ ___/ | | | | \// __ \\ \___| <\ ___/| | \/
  486. // |____| |__|__|_| /\___ > |____| |__| (____ /\___ >__|_ \\___ >__|
  487. // \/ \/ \/ \/ \/ \/
  488. // AddTimeManuallyForm form that adds spent time manually.
  489. type AddTimeManuallyForm struct {
  490. Hours int `binding:"Range(0,1000)"`
  491. Minutes int `binding:"Range(0,1000)"`
  492. }
  493. // Validate validates the fields
  494. func (f *AddTimeManuallyForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  495. return validate(errs, ctx.Data, f, ctx.Locale)
  496. }
  497. // SaveTopicForm form for save topics for repository
  498. type SaveTopicForm struct {
  499. Topics []string `binding:"topics;Required;"`
  500. }
  501. // DeadlineForm hold the validation rules for deadlines
  502. type DeadlineForm struct {
  503. DateString string `form:"date" binding:"Required;Size(10)"`
  504. }
  505. // Validate validates the fields
  506. func (f *DeadlineForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  507. return validate(errs, ctx.Data, f, ctx.Locale)
  508. }