forms.py 27 KB

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