forms.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2014-2016 - Copyright Red Hat Inc
  4. Authors:
  5. Pierre-Yves Chibon <pingou@pingoured.fr>
  6. """
  7. # pylint: disable=too-few-public-methods
  8. # pylint: disable=no-init
  9. # pylint: disable=super-on-old-class
  10. from __future__ import absolute_import, unicode_literals
  11. import datetime
  12. import re
  13. import flask
  14. import flask_wtf as wtf
  15. try:
  16. from flask_wtf import FlaskForm
  17. except ImportError:
  18. from flask_wtf import Form as FlaskForm
  19. import six
  20. import wtforms
  21. import pagure.lib.query
  22. import pagure.validators
  23. from pagure.config import config as pagure_config
  24. from pagure.utils import is_admin, urlpattern
  25. STRICT_REGEX = "^[a-zA-Z0-9-_]+$"
  26. # This regex is used when creating tags, there we do not want to allow ','
  27. # as otherwise it breaks the UI.
  28. TAGS_REGEX = "^[a-zA-Z0-9][a-zA-Z0-9-_ .:]+$"
  29. TAGS_REGEX_RE = re.compile(TAGS_REGEX)
  30. # In the issue page tags are sent as a comma-separated list, so in order to
  31. # allow having multiple tags in an issue, we need to allow ',' in them.
  32. TAGS_REGEX_MULTI = "^[a-zA-Z0-9][a-zA-Z0-9-_, .:]+$"
  33. FALSE_VALUES = ("false", "", False, "False", 0, "0")
  34. WTF_VERSION = tuple()
  35. if hasattr(wtf, "__version__"):
  36. WTF_VERSION = tuple(int(v) for v in wtf.__version__.split("."))
  37. class PagureForm(FlaskForm):
  38. """Local form allowing us to form set the time limit."""
  39. def __init__(self, *args, **kwargs):
  40. delta = pagure_config.get("WTF_CSRF_TIME_LIMIT", 3600)
  41. if delta and WTF_VERSION < (0, 10, 0):
  42. self.TIME_LIMIT = datetime.timedelta(seconds=delta)
  43. else:
  44. self.TIME_LIMIT = delta
  45. if "csrf_enabled" in kwargs and kwargs["csrf_enabled"] is False:
  46. kwargs["meta"] = {"csrf": False}
  47. if WTF_VERSION >= (0, 14, 0):
  48. kwargs.pop("csrf_enabled")
  49. super(PagureForm, self).__init__(*args, **kwargs)
  50. def convert_value(val):
  51. """Convert the provided values to strings when possible."""
  52. if val:
  53. if not isinstance(val, (list, tuple, six.text_type)):
  54. return val.decode("utf-8")
  55. elif isinstance(val, six.string_types):
  56. return val
  57. class MultipleEmail(wtforms.validators.Email):
  58. """Split the value by comma and run them through the email validator
  59. of wtforms.
  60. """
  61. def __call__(self, form, field):
  62. message = field.gettext("One or more invalid email address.")
  63. for data in field.data.split(","):
  64. data = data.strip()
  65. if not self.regex.match(data or ""):
  66. raise wtforms.validators.ValidationError(message)
  67. def user_namespace_if_private(form, field):
  68. """Check if the data in the field is the same as in the password field."""
  69. if form.private.data:
  70. field.data = flask.g.fas_user.username
  71. def file_virus_validator(form, field):
  72. """Checks for virus in the file from flask request object,
  73. raises wtf.ValidationError if virus is found else None."""
  74. if not pagure_config["VIRUS_SCAN_ATTACHMENTS"]:
  75. return
  76. from pyclamd import ClamdUnixSocket
  77. if (
  78. field.name not in flask.request.files
  79. or flask.request.files[field.name].filename == ""
  80. ):
  81. # If no file was uploaded, this field is correct
  82. return
  83. uploaded = flask.request.files[field.name]
  84. clam = ClamdUnixSocket()
  85. if not clam.ping():
  86. raise wtforms.ValidationError(
  87. "Unable to communicate with virus scanner"
  88. )
  89. results = clam.scan_stream(uploaded.stream.read())
  90. if results is None:
  91. uploaded.stream.seek(0)
  92. return
  93. else:
  94. result = results.values()
  95. res_type, res_msg = result
  96. if res_type == "FOUND":
  97. raise wtforms.ValidationError("Virus found: %s" % res_msg)
  98. else:
  99. raise wtforms.ValidationError("Error scanning uploaded file")
  100. def ssh_key_validator(form, field):
  101. """Form for ssh key validation"""
  102. if not pagure.lib.query.are_valid_ssh_keys(field.data):
  103. raise wtforms.ValidationError("Invalid SSH keys")
  104. class ProjectFormSimplified(PagureForm):
  105. """Form to edit the description of a project."""
  106. description = wtforms.StringField(
  107. "Description",
  108. [wtforms.validators.DataRequired()],
  109. )
  110. url = wtforms.StringField(
  111. "URL",
  112. [
  113. wtforms.validators.optional(),
  114. wtforms.validators.Regexp(urlpattern, flags=re.IGNORECASE),
  115. ],
  116. )
  117. avatar_email = wtforms.StringField(
  118. "Avatar email",
  119. [
  120. pagure.validators.EmailValidator("avatar_email must be an email"),
  121. wtforms.validators.optional(),
  122. ],
  123. )
  124. tags = wtforms.StringField(
  125. "Project tags",
  126. [wtforms.validators.optional(), wtforms.validators.Length(max=255)],
  127. )
  128. private = wtforms.BooleanField(
  129. "Private", [wtforms.validators.Optional()], false_values=FALSE_VALUES
  130. )
  131. mirrored_from = wtforms.StringField(
  132. "Mirrored from",
  133. [wtforms.validators.optional(), wtforms.validators.Length(max=255)],
  134. )
  135. class ProjectForm(ProjectFormSimplified):
  136. """Form to create or edit project."""
  137. name = wtforms.StringField("Project name")
  138. mirrored_from = wtforms.StringField(
  139. "Mirror from URL",
  140. [
  141. wtforms.validators.optional(),
  142. wtforms.validators.Regexp(urlpattern, flags=re.IGNORECASE),
  143. ],
  144. )
  145. create_readme = wtforms.BooleanField(
  146. "Create README",
  147. [wtforms.validators.optional()],
  148. false_values=FALSE_VALUES,
  149. )
  150. namespace = wtforms.SelectField(
  151. "Project Namespace",
  152. [user_namespace_if_private, wtforms.validators.optional()],
  153. choices=[],
  154. coerce=convert_value,
  155. )
  156. ignore_existing_repos = wtforms.BooleanField(
  157. "Ignore existing repositories",
  158. [wtforms.validators.optional()],
  159. false_values=FALSE_VALUES,
  160. )
  161. default_branch = wtforms.StringField(
  162. "Default branch",
  163. [wtforms.validators.optional()],
  164. )
  165. def __init__(self, *args, **kwargs):
  166. """Calls the default constructor with the normal argument but
  167. uses the list of collection provided to fill the choices of the
  168. drop-down list.
  169. """
  170. super(ProjectForm, self).__init__(*args, **kwargs)
  171. # set the name validator
  172. regex = pagure_config.get(
  173. "PROJECT_NAME_REGEX", "^[a-zA-z0-9_][a-zA-Z0-9-_.+]*$"
  174. )
  175. self.name.validators = [
  176. wtforms.validators.DataRequired(),
  177. wtforms.validators.Regexp(regex, flags=re.IGNORECASE),
  178. ]
  179. # Set the list of namespace
  180. if "namespaces" in kwargs:
  181. self.namespace.choices = [
  182. (namespace, namespace) for namespace in kwargs["namespaces"]
  183. ]
  184. if not pagure_config.get("USER_NAMESPACE", False):
  185. self.namespace.choices.insert(0, ("", ""))
  186. if not (
  187. is_admin()
  188. and pagure_config.get("ALLOW_ADMIN_IGNORE_EXISTING_REPOS")
  189. ) and (
  190. flask.g.fas_user.username
  191. not in pagure_config["USERS_IGNORE_EXISTING_REPOS"]
  192. ):
  193. self.ignore_existing_repos = None
  194. class IssueFormSimplied(PagureForm):
  195. """Form to create or edit an issue."""
  196. title = wtforms.StringField(
  197. "Title",
  198. [wtforms.validators.DataRequired()],
  199. )
  200. issue_content = wtforms.TextAreaField(
  201. "Content",
  202. [wtforms.validators.DataRequired()],
  203. )
  204. private = wtforms.BooleanField(
  205. "Private", [wtforms.validators.optional()], false_values=FALSE_VALUES
  206. )
  207. milestone = wtforms.SelectField(
  208. "Milestone",
  209. [wtforms.validators.Optional()],
  210. choices=[],
  211. coerce=convert_value,
  212. )
  213. priority = wtforms.SelectField(
  214. "Priority",
  215. [wtforms.validators.Optional()],
  216. choices=[],
  217. coerce=convert_value,
  218. )
  219. def __init__(self, *args, **kwargs):
  220. """Calls the default constructor with the normal argument but
  221. uses the list of collection provided to fill the choices of the
  222. drop-down list.
  223. """
  224. super(IssueFormSimplied, self).__init__(*args, **kwargs)
  225. self.priority.choices = []
  226. if "priorities" in kwargs:
  227. for key in sorted(kwargs["priorities"]):
  228. self.priority.choices.append((key, kwargs["priorities"][key]))
  229. self.milestone.choices = []
  230. if "milestones" in kwargs and kwargs["milestones"]:
  231. for key in kwargs["milestones"]:
  232. self.milestone.choices.append((key, key))
  233. self.milestone.choices.insert(0, ("", ""))
  234. class IssueForm(IssueFormSimplied):
  235. """Form to create or edit an issue."""
  236. status = wtforms.SelectField(
  237. "Status", [wtforms.validators.DataRequired()], choices=[]
  238. )
  239. def __init__(self, *args, **kwargs):
  240. """Calls the default constructor with the normal argument but
  241. uses the list of collection provided to fill the choices of the
  242. drop-down list.
  243. """
  244. super(IssueForm, self).__init__(*args, **kwargs)
  245. if "status" in kwargs:
  246. self.status.choices = [
  247. (status, status) for status in kwargs["status"]
  248. ]
  249. class RequestPullForm(PagureForm):
  250. """Form to create a pull request."""
  251. title = wtforms.StringField(
  252. "Title",
  253. [wtforms.validators.DataRequired()],
  254. )
  255. initial_comment = wtforms.TextAreaField(
  256. "Initial Comment", [wtforms.validators.Optional()]
  257. )
  258. allow_rebase = wtforms.BooleanField(
  259. "Allow rebasing",
  260. [wtforms.validators.Optional()],
  261. false_values=FALSE_VALUES,
  262. )
  263. class RequestPullEditForm(RequestPullForm):
  264. """Form to edit a pull request."""
  265. branch_to = wtforms.SelectField(
  266. "Target branch",
  267. [wtforms.validators.DataRequired()],
  268. choices=[],
  269. coerce=convert_value,
  270. )
  271. def __init__(self, *args, **kwargs):
  272. """Calls the default constructor with the normal argument but
  273. uses the list of collection provided to fill the choices of the
  274. drop-down list.
  275. """
  276. super(RequestPullEditForm, self).__init__(*args, **kwargs)
  277. if "branches" in kwargs:
  278. self.branch_to.choices = [
  279. (branch, branch) for branch in kwargs["branches"]
  280. ]
  281. class RemoteRequestPullForm(RequestPullForm):
  282. """Form to create a remote pull request."""
  283. git_repo = wtforms.StringField(
  284. "Git repo address",
  285. [
  286. wtforms.validators.DataRequired(),
  287. wtforms.validators.Regexp(urlpattern, flags=re.IGNORECASE),
  288. ],
  289. )
  290. branch_from = wtforms.StringField(
  291. "Git branch",
  292. [wtforms.validators.DataRequired()],
  293. )
  294. branch_to = wtforms.StringField(
  295. "Git branch to merge in",
  296. [wtforms.validators.DataRequired()],
  297. )
  298. class DeleteIssueTagForm(PagureForm):
  299. """Form to remove a tag to from a project."""
  300. tag = wtforms.StringField(
  301. "Tag",
  302. [
  303. wtforms.validators.Optional(),
  304. wtforms.validators.Regexp(TAGS_REGEX, flags=re.IGNORECASE),
  305. wtforms.validators.Length(max=255),
  306. ],
  307. )
  308. class AddIssueTagForm(DeleteIssueTagForm):
  309. """Form to add a tag to a project."""
  310. tag_description = wtforms.StringField(
  311. "Tag Description", [wtforms.validators.Optional()]
  312. )
  313. tag_color = wtforms.StringField(
  314. "Tag Color", [wtforms.validators.DataRequired()]
  315. )
  316. class ApiAddIssueTagForm(PagureForm):
  317. """Form to add a tag to a project from the API endpoint"""
  318. tag = wtforms.StringField(
  319. "Tag",
  320. [
  321. wtforms.validators.DataRequired(),
  322. wtforms.validators.Regexp(TAGS_REGEX, flags=re.IGNORECASE),
  323. wtforms.validators.Length(max=255),
  324. ],
  325. )
  326. tag_description = wtforms.StringField(
  327. "Tag Description", [wtforms.validators.Optional()]
  328. )
  329. tag_color = wtforms.StringField(
  330. "Tag Color", [wtforms.validators.DataRequired()]
  331. )
  332. class StatusForm(PagureForm):
  333. """Form to add/change the status of an issue."""
  334. status = wtforms.SelectField(
  335. "Status", [wtforms.validators.DataRequired()], choices=[]
  336. )
  337. close_status = wtforms.SelectField(
  338. "Closed as", [wtforms.validators.Optional()], choices=[]
  339. )
  340. def __init__(self, *args, **kwargs):
  341. """Calls the default constructor with the normal argument but
  342. uses the list of collection provided to fill the choices of the
  343. drop-down list.
  344. """
  345. super(StatusForm, self).__init__(*args, **kwargs)
  346. if "status" in kwargs:
  347. self.status.choices = [
  348. (status, status) for status in kwargs["status"]
  349. ]
  350. self.close_status.choices = []
  351. if "close_status" in kwargs:
  352. for key in sorted(kwargs["close_status"]):
  353. self.close_status.choices.append((key, key))
  354. self.close_status.choices.insert(0, ("", ""))
  355. class MilestoneForm(PagureForm):
  356. """Form to change the milestone of an issue."""
  357. milestone = wtforms.SelectField(
  358. "Milestone",
  359. [wtforms.validators.Optional()],
  360. choices=[],
  361. coerce=convert_value,
  362. )
  363. def __init__(self, *args, **kwargs):
  364. """Calls the default constructor with the normal argument but
  365. uses the list of collection provided to fill the choices of the
  366. drop-down list.
  367. """
  368. super(MilestoneForm, self).__init__(*args, **kwargs)
  369. self.milestone.choices = []
  370. if "milestones" in kwargs and kwargs["milestones"]:
  371. for key in kwargs["milestones"]:
  372. self.milestone.choices.append((key, key))
  373. self.milestone.choices.insert(0, ("", ""))
  374. class NewTokenForm(PagureForm):
  375. """Form to add a new token."""
  376. description = wtforms.StringField(
  377. "description", [wtforms.validators.Optional()]
  378. )
  379. expiration_date = wtforms.DateField(
  380. "expiration date",
  381. [wtforms.validators.DataRequired()],
  382. default=datetime.date.today() + datetime.timedelta(days=(30 * 6)),
  383. )
  384. acls = wtforms.SelectMultipleField(
  385. "ACLs", [wtforms.validators.DataRequired()], choices=[]
  386. )
  387. def __init__(self, *args, **kwargs):
  388. """Calls the default constructor with the normal argument but
  389. uses the list of collection provided to fill the choices of the
  390. drop-down list.
  391. """
  392. super(NewTokenForm, self).__init__(*args, **kwargs)
  393. if "acls" in kwargs:
  394. self.acls.choices = [
  395. (acl.name, acl.name) for acl in kwargs["acls"]
  396. ]
  397. if "sacls" in kwargs:
  398. self.acls.choices = [(acl, acl) for acl in kwargs["sacls"]]
  399. class UpdateIssueForm(PagureForm):
  400. """Form to add a comment to an issue."""
  401. tag = wtforms.StringField(
  402. "tag",
  403. [
  404. wtforms.validators.Optional(),
  405. wtforms.validators.Regexp(TAGS_REGEX_MULTI, flags=re.IGNORECASE),
  406. wtforms.validators.Length(max=255),
  407. ],
  408. )
  409. depending = wtforms.StringField(
  410. "depending issue", [wtforms.validators.Optional()]
  411. )
  412. blocking = wtforms.StringField(
  413. "blocking issue", [wtforms.validators.Optional()]
  414. )
  415. comment = wtforms.TextAreaField("Comment", [wtforms.validators.Optional()])
  416. assignee = wtforms.TextAreaField(
  417. "Assigned to", [wtforms.validators.Optional()]
  418. )
  419. status = wtforms.SelectField(
  420. "Status", [wtforms.validators.Optional()], choices=[]
  421. )
  422. priority = wtforms.SelectField(
  423. "Priority", [wtforms.validators.Optional()], choices=[]
  424. )
  425. milestone = wtforms.SelectField(
  426. "Milestone",
  427. [wtforms.validators.Optional()],
  428. choices=[],
  429. coerce=convert_value,
  430. )
  431. private = wtforms.BooleanField(
  432. "Private", [wtforms.validators.optional()], false_values=FALSE_VALUES
  433. )
  434. close_status = wtforms.SelectField(
  435. "Closed as",
  436. [wtforms.validators.Optional()],
  437. choices=[],
  438. coerce=convert_value,
  439. )
  440. def __init__(self, *args, **kwargs):
  441. """Calls the default constructor with the normal argument but
  442. uses the list of collection provided to fill the choices of the
  443. drop-down list.
  444. """
  445. super(UpdateIssueForm, self).__init__(*args, **kwargs)
  446. if "status" in kwargs:
  447. self.status.choices = [
  448. (status, status) for status in kwargs["status"]
  449. ]
  450. self.priority.choices = []
  451. if "priorities" in kwargs:
  452. for key in sorted(kwargs["priorities"]):
  453. self.priority.choices.append((key, kwargs["priorities"][key]))
  454. self.milestone.choices = []
  455. if "milestones" in kwargs and kwargs["milestones"]:
  456. for key in kwargs["milestones"]:
  457. self.milestone.choices.append((key, key))
  458. self.milestone.choices.insert(0, ("", ""))
  459. self.close_status.choices = []
  460. if "close_status" in kwargs:
  461. for key in sorted(kwargs["close_status"]):
  462. self.close_status.choices.append((key, key))
  463. self.close_status.choices.insert(0, ("", ""))
  464. class AddPullRequestCommentForm(PagureForm):
  465. """Form to add a comment to a pull-request."""
  466. commit = wtforms.HiddenField("commit identifier")
  467. filename = wtforms.HiddenField("file changed")
  468. row = wtforms.HiddenField("row")
  469. requestid = wtforms.HiddenField("requestid")
  470. tree_id = wtforms.HiddenField("treeid")
  471. comment = wtforms.TextAreaField(
  472. "Comment",
  473. [wtforms.validators.DataRequired()],
  474. )
  475. class AddPullRequestFlagFormV1(PagureForm):
  476. """Form to add a flag to a pull-request or commit."""
  477. username = wtforms.StringField(
  478. "Username", [wtforms.validators.DataRequired()]
  479. )
  480. percent = wtforms.StringField(
  481. "Percentage of completion", [wtforms.validators.optional()]
  482. )
  483. comment = wtforms.TextAreaField(
  484. "Comment", [wtforms.validators.DataRequired()]
  485. )
  486. url = wtforms.StringField(
  487. "URL",
  488. [
  489. wtforms.validators.DataRequired(),
  490. wtforms.validators.Regexp(urlpattern, flags=re.IGNORECASE),
  491. ],
  492. )
  493. uid = wtforms.StringField("UID", [wtforms.validators.optional()])
  494. class AddPullRequestFlagForm(AddPullRequestFlagFormV1):
  495. """Form to add a flag to a pull-request or commit."""
  496. def __init__(self, *args, **kwargs):
  497. # we need to instantiate dynamically because the configuration
  498. # values may change during tests and we want to always respect
  499. # the currently set value
  500. super(AddPullRequestFlagForm, self).__init__(*args, **kwargs)
  501. self.status.choices = list(
  502. zip(
  503. pagure_config["FLAG_STATUSES_LABELS"].keys(),
  504. pagure_config["FLAG_STATUSES_LABELS"].keys(),
  505. )
  506. )
  507. status = wtforms.SelectField(
  508. "status", [wtforms.validators.DataRequired()], choices=[]
  509. )
  510. class AddSSHKeyForm(PagureForm):
  511. """Form to add a SSH key to a user."""
  512. ssh_key = wtforms.StringField(
  513. "SSH Key",
  514. [wtforms.validators.DataRequired()]
  515. # TODO: Add an ssh key validator?
  516. )
  517. class AddDeployKeyForm(AddSSHKeyForm):
  518. """Form to add a deploy key to a project."""
  519. pushaccess = wtforms.BooleanField(
  520. "Push access",
  521. [wtforms.validators.optional()],
  522. false_values=FALSE_VALUES,
  523. )
  524. class AddUserForm(PagureForm):
  525. """Form to add a user to a project."""
  526. user = wtforms.StringField(
  527. "Username",
  528. [wtforms.validators.DataRequired()],
  529. )
  530. access = wtforms.StringField(
  531. "Access Level",
  532. [wtforms.validators.DataRequired()],
  533. )
  534. branches = wtforms.StringField(
  535. "Git branches",
  536. [wtforms.validators.Optional()],
  537. )
  538. class AddUserToGroupForm(PagureForm):
  539. """Form to add a user to a pagure group."""
  540. user = wtforms.StringField(
  541. "Username",
  542. [wtforms.validators.DataRequired()],
  543. )
  544. class AssignIssueForm(PagureForm):
  545. """Form to assign an user to an issue."""
  546. assignee = wtforms.StringField(
  547. "Assignee",
  548. [wtforms.validators.Optional()],
  549. )
  550. class AddGroupForm(PagureForm):
  551. """Form to add a group to a project."""
  552. group = wtforms.StringField(
  553. "Group",
  554. [
  555. wtforms.validators.DataRequired(),
  556. wtforms.validators.Regexp(STRICT_REGEX, flags=re.IGNORECASE),
  557. ],
  558. )
  559. access = wtforms.StringField(
  560. "Access Level",
  561. [wtforms.validators.DataRequired()],
  562. )
  563. branches = wtforms.StringField(
  564. "Git branches",
  565. [wtforms.validators.Optional()],
  566. )
  567. class ConfirmationForm(PagureForm):
  568. """Simple form used just for CSRF protection."""
  569. pass
  570. class ModifyACLForm(PagureForm):
  571. """Form to change ACL of a user or a group to a project."""
  572. user_type = wtforms.SelectField(
  573. "User type",
  574. [wtforms.validators.DataRequired()],
  575. choices=[("user", "User"), ("group", "Group")],
  576. )
  577. name = wtforms.StringField(
  578. "User- or Groupname",
  579. [wtforms.validators.DataRequired()],
  580. )
  581. acl = wtforms.SelectField(
  582. "ACL type",
  583. [wtforms.validators.Optional()],
  584. choices=[
  585. ("admin", "Admin"),
  586. ("ticket", "Ticket"),
  587. ("commit", "Commit"),
  588. (None, None),
  589. ],
  590. coerce=convert_value,
  591. )
  592. class UploadFileForm(PagureForm):
  593. """Form to upload a file."""
  594. filestream = wtforms.FileField(
  595. "File", [wtforms.validators.DataRequired(), file_virus_validator]
  596. )
  597. class UserEmailForm(PagureForm):
  598. """Form to edit the description of a project."""
  599. email = wtforms.StringField("email", [wtforms.validators.DataRequired()])
  600. def __init__(self, *args, **kwargs):
  601. super(UserEmailForm, self).__init__(*args, **kwargs)
  602. if "emails" in kwargs:
  603. if kwargs["emails"]:
  604. self.email.validators.append(
  605. wtforms.validators.NoneOf(kwargs["emails"])
  606. )
  607. else:
  608. self.email.validators = [wtforms.validators.DataRequired()]
  609. class ProjectCommentForm(PagureForm):
  610. """Form to represent project."""
  611. objid = wtforms.StringField(
  612. "Ticket/Request id", [wtforms.validators.DataRequired()]
  613. )
  614. useremail = wtforms.StringField(
  615. "Email", [wtforms.validators.DataRequired()]
  616. )
  617. class CommentForm(PagureForm):
  618. """Form to upload a file."""
  619. comment = wtforms.FileField(
  620. "Comment", [wtforms.validators.DataRequired(), file_virus_validator]
  621. )
  622. class EditGroupForm(PagureForm):
  623. """Form to ask for a password change."""
  624. display_name = wtforms.StringField(
  625. "Group name to display",
  626. [
  627. wtforms.validators.DataRequired(),
  628. wtforms.validators.Length(max=255),
  629. ],
  630. )
  631. description = wtforms.StringField(
  632. "Description",
  633. [
  634. wtforms.validators.DataRequired(),
  635. wtforms.validators.Length(max=255),
  636. ],
  637. )
  638. class NewGroupForm(EditGroupForm):
  639. """Form to ask for a password change."""
  640. group_name = wtforms.StringField(
  641. "Group name",
  642. [
  643. wtforms.validators.DataRequired(),
  644. wtforms.validators.Length(max=255),
  645. wtforms.validators.Regexp(STRICT_REGEX, flags=re.IGNORECASE),
  646. ],
  647. )
  648. group_type = wtforms.SelectField(
  649. "Group type", [wtforms.validators.DataRequired()], choices=[]
  650. )
  651. def __init__(self, *args, **kwargs):
  652. """Calls the default constructor with the normal argument but
  653. uses the list of collection provided to fill the choices of the
  654. drop-down list.
  655. """
  656. super(NewGroupForm, self).__init__(*args, **kwargs)
  657. if "group_types" in kwargs:
  658. self.group_type.choices = [
  659. (grptype, grptype) for grptype in kwargs["group_types"]
  660. ]
  661. class EditFileForm(PagureForm):
  662. """Form used to edit a file."""
  663. content = wtforms.TextAreaField("content", [wtforms.validators.Optional()])
  664. commit_title = wtforms.StringField(
  665. "Title", [wtforms.validators.DataRequired()]
  666. )
  667. commit_message = wtforms.TextAreaField(
  668. "Commit message", [wtforms.validators.optional()]
  669. )
  670. email = wtforms.SelectField(
  671. "Email", [wtforms.validators.DataRequired()], choices=[]
  672. )
  673. branch = wtforms.StringField("Branch", [wtforms.validators.DataRequired()])
  674. def __init__(self, *args, **kwargs):
  675. """Calls the default constructor with the normal argument but
  676. uses the list of collection provided to fill the choices of the
  677. drop-down list.
  678. """
  679. super(EditFileForm, self).__init__(*args, **kwargs)
  680. if "emails" in kwargs:
  681. self.email.choices = [
  682. (email.email, email.email) for email in kwargs["emails"]
  683. ]
  684. class DefaultBranchForm(PagureForm):
  685. """Form to change the default branh for a repository"""
  686. branches = wtforms.SelectField(
  687. "default_branch", [wtforms.validators.DataRequired()], choices=[]
  688. )
  689. def __init__(self, *args, **kwargs):
  690. """Calls the default constructor with the normal argument but
  691. uses the list of collection provided to fill the choices of the
  692. drop-down list.
  693. """
  694. super(DefaultBranchForm, self).__init__(*args, **kwargs)
  695. if "branches" in kwargs:
  696. self.branches.choices = [
  697. (branch, branch) for branch in kwargs["branches"]
  698. ]
  699. class DefaultPriorityForm(PagureForm):
  700. """Form to change the default priority for a repository"""
  701. priority = wtforms.SelectField(
  702. "default_priority", [wtforms.validators.optional()], choices=[]
  703. )
  704. def __init__(self, *args, **kwargs):
  705. """Calls the default constructor with the normal argument but
  706. uses the list of collection provided to fill the choices of the
  707. drop-down list.
  708. """
  709. super(DefaultPriorityForm, self).__init__(*args, **kwargs)
  710. if "priorities" in kwargs:
  711. self.priority.choices = [
  712. (priority, priority) for priority in kwargs["priorities"]
  713. ]
  714. class EditCommentForm(PagureForm):
  715. """Form to verify that comment is not empty"""
  716. update_comment = wtforms.TextAreaField(
  717. "Comment ",
  718. [wtforms.validators.DataRequired()],
  719. )
  720. class ForkRepoForm(PagureForm):
  721. """Form to fork a project in the API."""
  722. repo = wtforms.StringField(
  723. "The project name", [wtforms.validators.DataRequired()]
  724. )
  725. username = wtforms.StringField(
  726. "User who forked the project", [wtforms.validators.optional()]
  727. )
  728. namespace = wtforms.StringField(
  729. "The project namespace", [wtforms.validators.optional()]
  730. )
  731. class AddReportForm(PagureForm):
  732. """Form to verify that comment is not empty"""
  733. report_name = wtforms.TextAreaField(
  734. "Report name",
  735. [wtforms.validators.DataRequired()],
  736. )
  737. class PublicNotificationForm(PagureForm):
  738. """Form to verify that comment is not empty"""
  739. issue_notifs = wtforms.TextAreaField(
  740. "Public issue notification",
  741. [wtforms.validators.optional(), MultipleEmail()],
  742. )
  743. pr_notifs = wtforms.TextAreaField(
  744. "Public PR notification",
  745. [wtforms.validators.optional(), MultipleEmail()],
  746. )
  747. class SubscribtionForm(PagureForm):
  748. """Form to subscribe to or unsubscribe from an issue or a PR."""
  749. status = wtforms.BooleanField(
  750. "Subscription status",
  751. [wtforms.validators.optional()],
  752. false_values=FALSE_VALUES,
  753. )
  754. class MergePRForm(PagureForm):
  755. delete_branch = wtforms.BooleanField(
  756. "Delete branch after merging",
  757. [wtforms.validators.optional()],
  758. false_values=FALSE_VALUES,
  759. )
  760. class TriggerCIPRForm(PagureForm):
  761. def __init__(self, *args, **kwargs):
  762. # we need to instantiate dynamically because the configuration
  763. # values may change during tests and we want to always respect
  764. # the currently set value
  765. super(TriggerCIPRForm, self).__init__(*args, **kwargs)
  766. choices = []
  767. trigger_ci = pagure_config["TRIGGER_CI"]
  768. if isinstance(trigger_ci, dict):
  769. # make sure to preserver compatibility with older configs
  770. # which had TRIGGER_CI as a list
  771. for comment, meta in trigger_ci.items():
  772. if meta is not None:
  773. choices.append((comment, comment))
  774. self.comment.choices = choices
  775. comment = wtforms.SelectField(
  776. "comment", [wtforms.validators.DataRequired()], choices=[]
  777. )
  778. class AddGitTagForm(PagureForm):
  779. """Form to create a new git tag."""
  780. tagname = wtforms.StringField(
  781. "Name of the tag",
  782. [wtforms.validators.DataRequired()],
  783. )
  784. commit_hash = wtforms.StringField(
  785. "Hash of the commit to tag", [wtforms.validators.DataRequired()]
  786. )
  787. message = wtforms.TextAreaField(
  788. "Annotation message", [wtforms.validators.Optional()]
  789. )
  790. force = wtforms.BooleanField(
  791. "Force the creation of the git tag",
  792. [wtforms.validators.optional()],
  793. false_values=FALSE_VALUES,
  794. )