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 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  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. macaron "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. PullsAllowRebaseMerge bool
  102. PullsAllowSquash bool
  103. EnableTimetracker bool
  104. AllowOnlyContributorsToTrackTime bool
  105. EnableIssueDependencies bool
  106. IsArchived bool
  107. // Admin settings
  108. EnableHealthCheck bool
  109. EnableCloseIssuesViaCommitInAnyBranch bool
  110. }
  111. // Validate validates the fields
  112. func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  113. return validate(errs, ctx.Data, f, ctx.Locale)
  114. }
  115. // __________ .__
  116. // \______ \____________ ____ ____ | |__
  117. // | | _/\_ __ \__ \ / \_/ ___\| | \
  118. // | | \ | | \// __ \| | \ \___| Y \
  119. // |______ / |__| (____ /___| /\___ >___| /
  120. // \/ \/ \/ \/ \/
  121. // ProtectBranchForm form for changing protected branch settings
  122. type ProtectBranchForm struct {
  123. Protected bool
  124. EnableWhitelist bool
  125. WhitelistUsers string
  126. WhitelistTeams string
  127. EnableMergeWhitelist bool
  128. MergeWhitelistUsers string
  129. MergeWhitelistTeams string
  130. RequiredApprovals int64
  131. ApprovalsWhitelistUsers string
  132. ApprovalsWhitelistTeams string
  133. }
  134. // Validate validates the fields
  135. func (f *ProtectBranchForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  136. return validate(errs, ctx.Data, f, ctx.Locale)
  137. }
  138. // __ __ ___. .__ .__ __
  139. // / \ / \ ____\_ |__ | |__ | |__ ____ | | __
  140. // \ \/\/ // __ \| __ \| | \| | \ / _ \| |/ /
  141. // \ /\ ___/| \_\ \ Y \ Y ( <_> ) <
  142. // \__/\ / \___ >___ /___| /___| /\____/|__|_ \
  143. // \/ \/ \/ \/ \/ \/
  144. // WebhookForm form for changing web hook
  145. type WebhookForm struct {
  146. Events string
  147. Create bool
  148. Delete bool
  149. Fork bool
  150. Issues bool
  151. IssueComment bool
  152. Release bool
  153. Push bool
  154. PullRequest bool
  155. Repository bool
  156. Active bool
  157. }
  158. // PushOnly if the hook will be triggered when push
  159. func (f WebhookForm) PushOnly() bool {
  160. return f.Events == "push_only"
  161. }
  162. // SendEverything if the hook will be triggered any event
  163. func (f WebhookForm) SendEverything() bool {
  164. return f.Events == "send_everything"
  165. }
  166. // ChooseEvents if the hook will be triggered choose events
  167. func (f WebhookForm) ChooseEvents() bool {
  168. return f.Events == "choose_events"
  169. }
  170. // NewWebhookForm form for creating web hook
  171. type NewWebhookForm struct {
  172. PayloadURL string `binding:"Required;ValidUrl"`
  173. ContentType int `binding:"Required"`
  174. Secret string
  175. WebhookForm
  176. }
  177. // Validate validates the fields
  178. func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  179. return validate(errs, ctx.Data, f, ctx.Locale)
  180. }
  181. // NewGogshookForm form for creating gogs hook
  182. type NewGogshookForm struct {
  183. PayloadURL string `binding:"Required;ValidUrl"`
  184. ContentType int `binding:"Required"`
  185. Secret string
  186. WebhookForm
  187. }
  188. // Validate validates the fields
  189. func (f *NewGogshookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  190. return validate(errs, ctx.Data, f, ctx.Locale)
  191. }
  192. // NewSlackHookForm form for creating slack hook
  193. type NewSlackHookForm struct {
  194. PayloadURL string `binding:"Required;ValidUrl"`
  195. Channel string `binding:"Required"`
  196. Username string
  197. IconURL string
  198. Color string
  199. WebhookForm
  200. }
  201. // Validate validates the fields
  202. func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  203. return validate(errs, ctx.Data, f, ctx.Locale)
  204. }
  205. // HasInvalidChannel validates the channel name is in the right format
  206. func (f NewSlackHookForm) HasInvalidChannel() bool {
  207. return !utils.IsValidSlackChannel(f.Channel)
  208. }
  209. // NewDiscordHookForm form for creating discord hook
  210. type NewDiscordHookForm struct {
  211. PayloadURL string `binding:"Required;ValidUrl"`
  212. Username string
  213. IconURL string
  214. WebhookForm
  215. }
  216. // Validate validates the fields
  217. func (f *NewDiscordHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  218. return validate(errs, ctx.Data, f, ctx.Locale)
  219. }
  220. // NewDingtalkHookForm form for creating dingtalk hook
  221. type NewDingtalkHookForm struct {
  222. PayloadURL string `binding:"Required;ValidUrl"`
  223. WebhookForm
  224. }
  225. // Validate validates the fields
  226. func (f *NewDingtalkHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  227. return validate(errs, ctx.Data, f, ctx.Locale)
  228. }
  229. // .___
  230. // | | ______ ________ __ ____
  231. // | |/ ___// ___/ | \_/ __ \
  232. // | |\___ \ \___ \| | /\ ___/
  233. // |___/____ >____ >____/ \___ >
  234. // \/ \/ \/
  235. // CreateIssueForm form for creating issue
  236. type CreateIssueForm struct {
  237. Title string `binding:"Required;MaxSize(255)"`
  238. LabelIDs string `form:"label_ids"`
  239. AssigneeIDs string `form:"assignee_ids"`
  240. Ref string `form:"ref"`
  241. MilestoneID int64
  242. AssigneeID int64
  243. Content string
  244. Files []string
  245. }
  246. // Validate validates the fields
  247. func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  248. return validate(errs, ctx.Data, f, ctx.Locale)
  249. }
  250. // CreateCommentForm form for creating comment
  251. type CreateCommentForm struct {
  252. Content string
  253. Status string `binding:"OmitEmpty;In(reopen,close)"`
  254. Files []string
  255. }
  256. // Validate validates the fields
  257. func (f *CreateCommentForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  258. return validate(errs, ctx.Data, f, ctx.Locale)
  259. }
  260. // ReactionForm form for adding and removing reaction
  261. type ReactionForm struct {
  262. Content string `binding:"Required;In(+1,-1,laugh,confused,heart,hooray)"`
  263. }
  264. // Validate validates the fields
  265. func (f *ReactionForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  266. return validate(errs, ctx.Data, f, ctx.Locale)
  267. }
  268. // _____ .__.__ __
  269. // / \ |__| | ____ _______/ |_ ____ ____ ____
  270. // / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \
  271. // / Y \ | |_\ ___/ \___ \ | | ( <_> ) | \ ___/
  272. // \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ >
  273. // \/ \/ \/ \/ \/
  274. // CreateMilestoneForm form for creating milestone
  275. type CreateMilestoneForm struct {
  276. Title string `binding:"Required;MaxSize(50)"`
  277. Content string
  278. Deadline string
  279. }
  280. // Validate validates the fields
  281. func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  282. return validate(errs, ctx.Data, f, ctx.Locale)
  283. }
  284. // .____ ___. .__
  285. // | | _____ \_ |__ ____ | |
  286. // | | \__ \ | __ \_/ __ \| |
  287. // | |___ / __ \| \_\ \ ___/| |__
  288. // |_______ (____ /___ /\___ >____/
  289. // \/ \/ \/ \/
  290. // CreateLabelForm form for creating label
  291. type CreateLabelForm struct {
  292. ID int64
  293. Title string `binding:"Required;MaxSize(50)" locale:"repo.issues.label_title"`
  294. Description string `binding:"MaxSize(200)" locale:"repo.issues.label_description"`
  295. Color string `binding:"Required;Size(7)" locale:"repo.issues.label_color"`
  296. }
  297. // Validate validates the fields
  298. func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  299. return validate(errs, ctx.Data, f, ctx.Locale)
  300. }
  301. // InitializeLabelsForm form for initializing labels
  302. type InitializeLabelsForm struct {
  303. TemplateName string `binding:"Required"`
  304. }
  305. // Validate validates the fields
  306. func (f *InitializeLabelsForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  307. return validate(errs, ctx.Data, f, ctx.Locale)
  308. }
  309. // __________ .__ .__ __________ __
  310. // \______ \__ __| | | | \______ \ ____ ________ __ ____ _______/ |_
  311. // | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\
  312. // | | | | / |_| |__ | | \ ___< <_| | | /\ ___/ \___ \ | |
  313. // |____| |____/|____/____/ |____|_ /\___ >__ |____/ \___ >____ > |__|
  314. // \/ \/ |__| \/ \/
  315. // MergePullRequestForm form for merging Pull Request
  316. // swagger:model MergePullRequestOption
  317. type MergePullRequestForm struct {
  318. // required: true
  319. // enum: merge,rebase,rebase-merge,squash
  320. Do string `binding:"Required;In(merge,rebase,rebase-merge,squash)"`
  321. MergeTitleField string
  322. MergeMessageField string
  323. }
  324. // Validate validates the fields
  325. func (f *MergePullRequestForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  326. return validate(errs, ctx.Data, f, ctx.Locale)
  327. }
  328. // CodeCommentForm form for adding code comments for PRs
  329. type CodeCommentForm struct {
  330. Content string `binding:"Required"`
  331. Side string `binding:"Required;In(previous,proposed)"`
  332. Line int64
  333. TreePath string `form:"path" binding:"Required"`
  334. IsReview bool `form:"is_review"`
  335. Reply int64 `form:"reply"`
  336. }
  337. // Validate validates the fields
  338. func (f *CodeCommentForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  339. return validate(errs, ctx.Data, f, ctx.Locale)
  340. }
  341. // SubmitReviewForm for submitting a finished code review
  342. type SubmitReviewForm struct {
  343. Content string
  344. Type string `binding:"Required;In(approve,comment,reject)"`
  345. }
  346. // Validate validates the fields
  347. func (f *SubmitReviewForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  348. return validate(errs, ctx.Data, f, ctx.Locale)
  349. }
  350. // ReviewType will return the corresponding reviewtype for type
  351. func (f SubmitReviewForm) ReviewType() models.ReviewType {
  352. switch f.Type {
  353. case "approve":
  354. return models.ReviewTypeApprove
  355. case "comment":
  356. return models.ReviewTypeComment
  357. case "reject":
  358. return models.ReviewTypeReject
  359. default:
  360. return models.ReviewTypeUnknown
  361. }
  362. }
  363. // HasEmptyContent checks if the content of the review form is empty.
  364. func (f SubmitReviewForm) HasEmptyContent() bool {
  365. reviewType := f.ReviewType()
  366. return (reviewType == models.ReviewTypeComment || reviewType == models.ReviewTypeReject) &&
  367. len(strings.TrimSpace(f.Content)) == 0
  368. }
  369. // __________ .__
  370. // \______ \ ____ | | ____ _____ ______ ____
  371. // | _// __ \| | _/ __ \\__ \ / ___// __ \
  372. // | | \ ___/| |_\ ___/ / __ \_\___ \\ ___/
  373. // |____|_ /\___ >____/\___ >____ /____ >\___ >
  374. // \/ \/ \/ \/ \/ \/
  375. // NewReleaseForm form for creating release
  376. type NewReleaseForm struct {
  377. TagName string `binding:"Required;GitRefName"`
  378. Target string `form:"tag_target" binding:"Required"`
  379. Title string `binding:"Required"`
  380. Content string
  381. Draft string
  382. Prerelease bool
  383. Files []string
  384. }
  385. // Validate validates the fields
  386. func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  387. return validate(errs, ctx.Data, f, ctx.Locale)
  388. }
  389. // EditReleaseForm form for changing release
  390. type EditReleaseForm struct {
  391. Title string `form:"title" binding:"Required"`
  392. Content string `form:"content"`
  393. Draft string `form:"draft"`
  394. Prerelease bool `form:"prerelease"`
  395. Files []string
  396. }
  397. // Validate validates the fields
  398. func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  399. return validate(errs, ctx.Data, f, ctx.Locale)
  400. }
  401. // __ __.__ __ .__
  402. // / \ / \__| | _|__|
  403. // \ \/\/ / | |/ / |
  404. // \ /| | <| |
  405. // \__/\ / |__|__|_ \__|
  406. // \/ \/
  407. // NewWikiForm form for creating wiki
  408. type NewWikiForm struct {
  409. Title string `binding:"Required"`
  410. Content string `binding:"Required"`
  411. Message string
  412. }
  413. // Validate validates the fields
  414. // FIXME: use code generation to generate this method.
  415. func (f *NewWikiForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  416. return validate(errs, ctx.Data, f, ctx.Locale)
  417. }
  418. // ___________ .___.__ __
  419. // \_ _____/ __| _/|__|/ |_
  420. // | __)_ / __ | | \ __\
  421. // | \/ /_/ | | || |
  422. // /_______ /\____ | |__||__|
  423. // \/ \/
  424. // EditRepoFileForm form for changing repository file
  425. type EditRepoFileForm struct {
  426. TreePath string `binding:"Required;MaxSize(500)"`
  427. Content string `binding:"Required"`
  428. CommitSummary string `binding:"MaxSize(100)"`
  429. CommitMessage string
  430. CommitChoice string `binding:"Required;MaxSize(50)"`
  431. NewBranchName string `binding:"GitRefName;MaxSize(100)"`
  432. LastCommit string
  433. }
  434. // Validate validates the fields
  435. func (f *EditRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  436. return validate(errs, ctx.Data, f, ctx.Locale)
  437. }
  438. // EditPreviewDiffForm form for changing preview diff
  439. type EditPreviewDiffForm struct {
  440. Content string
  441. }
  442. // Validate validates the fields
  443. func (f *EditPreviewDiffForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  444. return validate(errs, ctx.Data, f, ctx.Locale)
  445. }
  446. // ____ ___ .__ .___
  447. // | | \______ | | _________ __| _/
  448. // | | /\____ \| | / _ \__ \ / __ |
  449. // | | / | |_> > |_( <_> ) __ \_/ /_/ |
  450. // |______/ | __/|____/\____(____ /\____ |
  451. // |__| \/ \/
  452. //
  453. // UploadRepoFileForm form for uploading repository file
  454. type UploadRepoFileForm struct {
  455. TreePath string `binding:"MaxSize(500)"`
  456. CommitSummary string `binding:"MaxSize(100)"`
  457. CommitMessage string
  458. CommitChoice string `binding:"Required;MaxSize(50)"`
  459. NewBranchName string `binding:"GitRefName;MaxSize(100)"`
  460. Files []string
  461. }
  462. // Validate validates the fields
  463. func (f *UploadRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  464. return validate(errs, ctx.Data, f, ctx.Locale)
  465. }
  466. // RemoveUploadFileForm form for removing uploaded file
  467. type RemoveUploadFileForm struct {
  468. File string `binding:"Required;MaxSize(50)"`
  469. }
  470. // Validate validates the fields
  471. func (f *RemoveUploadFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  472. return validate(errs, ctx.Data, f, ctx.Locale)
  473. }
  474. // ________ .__ __
  475. // \______ \ ____ | | _____/ |_ ____
  476. // | | \_/ __ \| | _/ __ \ __\/ __ \
  477. // | ` \ ___/| |_\ ___/| | \ ___/
  478. // /_______ /\___ >____/\___ >__| \___ >
  479. // \/ \/ \/ \/
  480. // DeleteRepoFileForm form for deleting repository file
  481. type DeleteRepoFileForm struct {
  482. CommitSummary string `binding:"MaxSize(100)"`
  483. CommitMessage string
  484. CommitChoice string `binding:"Required;MaxSize(50)"`
  485. NewBranchName string `binding:"GitRefName;MaxSize(100)"`
  486. }
  487. // Validate validates the fields
  488. func (f *DeleteRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  489. return validate(errs, ctx.Data, f, ctx.Locale)
  490. }
  491. // ___________.__ ___________ __
  492. // \__ ___/|__| _____ ____ \__ ___/___________ ____ | | __ ___________
  493. // | | | |/ \_/ __ \ | | \_ __ \__ \ _/ ___\| |/ // __ \_ __ \
  494. // | | | | Y Y \ ___/ | | | | \// __ \\ \___| <\ ___/| | \/
  495. // |____| |__|__|_| /\___ > |____| |__| (____ /\___ >__|_ \\___ >__|
  496. // \/ \/ \/ \/ \/ \/
  497. // AddTimeManuallyForm form that adds spent time manually.
  498. type AddTimeManuallyForm struct {
  499. Hours int `binding:"Range(0,1000)"`
  500. Minutes int `binding:"Range(0,1000)"`
  501. }
  502. // Validate validates the fields
  503. func (f *AddTimeManuallyForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  504. return validate(errs, ctx.Data, f, ctx.Locale)
  505. }
  506. // SaveTopicForm form for save topics for repository
  507. type SaveTopicForm struct {
  508. Topics []string `binding:"topics;Required;"`
  509. }
  510. // DeadlineForm hold the validation rules for deadlines
  511. type DeadlineForm struct {
  512. DateString string `form:"date" binding:"Required;Size(10)"`
  513. }
  514. // Validate validates the fields
  515. func (f *DeadlineForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
  516. return validate(errs, ctx.Data, f, ctx.Locale)
  517. }