Package coprs :: Module forms
[hide private]
[frames] | no frames]

Source Code for Module coprs.forms

   1  import re 
   2  from six.moves.urllib.parse import urlparse 
   3   
   4  import flask 
   5  import wtforms 
   6  import json 
   7   
   8  from flask_wtf.file import FileRequired, FileField 
   9  from fnmatch import fnmatch 
  10   
  11  try: # get rid of deprecation warning with newer flask_wtf 
  12      from flask_wtf import FlaskForm 
  13  except ImportError: 
  14      from flask_wtf import Form as FlaskForm 
  15   
  16  from coprs import constants 
  17  from coprs import app 
  18  from coprs import helpers 
  19  from coprs import models 
  20  from coprs.logic.coprs_logic import CoprsLogic, MockChrootsLogic 
  21  from coprs.logic.users_logic import UsersLogic 
  22  from coprs import exceptions 
  23   
  24   
  25  FALSE_VALUES = {False, "false", ""} 
26 27 28 -def get_package_form_cls_by_source_type_text(source_type_text):
29 """ 30 Params 31 ------ 32 source_type_text : str 33 name of the source type (scm/pypi/rubygems/git_and_tito/mock_scm) 34 35 Returns 36 ------- 37 BasePackageForm child 38 based on source_type_text input 39 """ 40 if source_type_text == 'scm': 41 return PackageFormScm 42 elif source_type_text == 'pypi': 43 return PackageFormPyPI 44 elif source_type_text == 'rubygems': 45 return PackageFormRubyGems 46 elif source_type_text == 'git_and_tito': 47 return PackageFormTito # deprecated 48 elif source_type_text == 'mock_scm': 49 return PackageFormMock # deprecated 50 elif source_type_text == "custom": 51 return PackageFormCustom 52 else: 53 raise exceptions.UnknownSourceTypeException("Invalid source type")
54
55 56 -class MultiCheckboxField(wtforms.SelectMultipleField):
57 widget = wtforms.widgets.ListWidget(prefix_label=False) 58 option_widget = wtforms.widgets.CheckboxInput()
59
60 61 -class UrlListValidator(object):
62
63 - def __init__(self, message=None):
64 if not message: 65 message = ("A list of http[s] URLs separated by whitespace characters" 66 " is needed ('{0}' doesn't seem to be a valid URL).") 67 self.message = message
68
69 - def __call__(self, form, field):
70 urls = field.data.split() 71 for u in urls: 72 if not self.is_url(u): 73 raise wtforms.ValidationError(self.message.format(u))
74
75 - def is_url(self, url):
76 parsed = urlparse(url) 77 if not parsed.scheme.startswith("http"): 78 return False 79 if not parsed.netloc: 80 return False 81 return True
82
83 84 -class UrlRepoListValidator(UrlListValidator):
85 """ Allows also `repo://` schema"""
86 - def is_url(self, url):
87 parsed = urlparse(url) 88 if parsed.scheme not in ["http", "https", "copr"]: 89 return False 90 if not parsed.netloc: 91 return False 92 # copr://username/projectname 93 # ^^ schema ^^ netlock ^^ path 94 if parsed.scheme == "copr": 95 # check if projectname missed 96 path_split = parsed.path.split("/") 97 if len(path_split) < 2 or path_split[1] == "": 98 return False 99 100 return True
101
102 103 -class UrlSrpmListValidator(UrlListValidator):
104 - def __init__(self, message=None):
105 if not message: 106 message = ("URLs must end with .src.rpm, .nosrc.rpm, or .spec" 107 " ('{0}' doesn't seem to be a valid URL).") 108 super(UrlSrpmListValidator, self).__init__(message)
109
110 - def is_url(self, url):
111 parsed = urlparse(url) 112 if not parsed.path.endswith((".src.rpm", ".nosrc.rpm", ".spec")): 113 return False 114 return True
115
116 117 -class SrpmValidator(object):
118 - def __init__(self, message=None):
119 if not message: 120 message = "You can upload only .src.rpm, .nosrc.rpm, and .spec files" 121 self.message = message
122
123 - def __call__(self, form, field):
124 filename = field.data.filename.lower() 125 if not filename.endswith((".src.rpm", ".nosrc.rpm", ".spec")): 126 raise wtforms.ValidationError(self.message)
127
128 129 -class CoprUniqueNameValidator(object):
130
131 - def __init__(self, message=None, user=None, group=None):
132 if not message: 133 if group is None: 134 message = "You already have project named '{}'." 135 else: 136 message = "Group {} ".format(group) + "already have project named '{}'." 137 self.message = message 138 if not user: 139 user = flask.g.user 140 self.user = user 141 self.group = group
142
143 - def __call__(self, form, field):
144 if self.group: 145 existing = CoprsLogic.exists_for_group( 146 self.group, field.data).first() 147 else: 148 existing = CoprsLogic.exists_for_user( 149 self.user, field.data).first() 150 151 if existing and str(existing.id) != form.id.data: 152 raise wtforms.ValidationError(self.message.format(field.data))
153
154 155 -class NameCharactersValidator(object):
156 - def __init__(self, message=None):
157 if not message: 158 message = "Name must contain only letters, digits, underscores, dashes and dots." 159 self.message = message
160
161 - def __call__(self, form, field):
162 validator = wtforms.validators.Regexp( 163 re.compile(r"^[\w.-]+$"), 164 message=self.message) 165 validator(form, field)
166
167 168 -class ChrootsValidator(object):
169 - def __call__(self, form, field):
170 # Allow it to be truly optional and has None value 171 if not field.data: 172 return 173 174 selected = set(field.data.split()) 175 enabled = set(MockChrootsLogic.active_names()) 176 177 if selected - enabled: 178 raise wtforms.ValidationError("Such chroot is not available: {}".format(", ".join(selected - enabled)))
179
180 181 -class NameNotNumberValidator(object):
182
183 - def __init__(self, message=None):
184 if not message: 185 message = "Project's name can not be just number." 186 self.message = message
187
188 - def __call__(self, form, field):
189 if field.data.isdigit(): 190 raise wtforms.ValidationError(self.message.format(field.data))
191
192 193 -class EmailOrURL(object):
194
195 - def __init__(self, message=None):
196 if not message: 197 message = "{} must be email address or URL" 198 self.message = message
199
200 - def __call__(self, form, field):
201 for validator in [wtforms.validators.Email(), wtforms.validators.URL()]: 202 try: 203 validator(form, field) 204 return True 205 except wtforms.ValidationError: 206 pass 207 raise wtforms.ValidationError(self.message.format(field.name.capitalize()))
208
209 210 -class StringListFilter(object):
211
212 - def __call__(self, value):
213 if not value: 214 return '' 215 # Replace every whitespace string with one newline 216 # Formats ideally for html form filling, use replace('\n', ' ') 217 # to get space-separated values or split() to get list 218 result = value.strip() 219 regex = re.compile(r"\s+") 220 return regex.sub(lambda x: '\n', result)
221
222 223 -class ValueToPermissionNumberFilter(object):
224
225 - def __call__(self, value):
226 if value: 227 return helpers.PermissionEnum("request") 228 return helpers.PermissionEnum("nothing")
229
230 231 -class CoprFormFactory(object):
232 233 @staticmethod
234 - def create_form_cls(mock_chroots=None, user=None, group=None, copr=None):
235 class F(FlaskForm): 236 # also use id here, to be able to find out whether user 237 # is updating a copr if so, we don't want to shout 238 # that name already exists 239 id = wtforms.HiddenField() 240 group_id = wtforms.HiddenField() 241 242 name = wtforms.StringField( 243 "Name", 244 validators=[ 245 wtforms.validators.DataRequired(), 246 NameCharactersValidator(), 247 CoprUniqueNameValidator(user=user, group=group), 248 NameNotNumberValidator() 249 ]) 250 251 homepage = wtforms.StringField( 252 "Homepage", 253 validators=[ 254 wtforms.validators.Optional(), 255 wtforms.validators.URL()]) 256 257 contact = wtforms.StringField( 258 "Contact", 259 validators=[ 260 wtforms.validators.Optional(), 261 EmailOrURL()]) 262 263 description = wtforms.TextAreaField("Description") 264 265 instructions = wtforms.TextAreaField("Instructions") 266 267 delete_after_days = wtforms.IntegerField( 268 "Delete after days", 269 validators=[ 270 wtforms.validators.Optional(), 271 wtforms.validators.NumberRange(min=0, max=60), 272 ], 273 render_kw={'disabled': bool(copr and copr.persistent)}) 274 275 repos = wtforms.TextAreaField( 276 "External Repositories", 277 validators=[UrlRepoListValidator()], 278 filters=[StringListFilter()]) 279 280 initial_pkgs = wtforms.TextAreaField( 281 "Initial packages to build", 282 validators=[ 283 UrlListValidator(), 284 UrlSrpmListValidator()], 285 filters=[StringListFilter()]) 286 287 disable_createrepo = wtforms.BooleanField(default=False, 288 label="Create repositories manually", 289 description="""Repository meta data is normally refreshed 290 after each build. If you want to do this manually, turn 291 this option on.""", 292 false_values=FALSE_VALUES) 293 294 unlisted_on_hp = wtforms.BooleanField( 295 "Project will not be listed on home page", 296 default=False, 297 false_values=FALSE_VALUES) 298 299 persistent = wtforms.BooleanField( 300 "Protect project and its builds against deletion", 301 description="""Project's builds and the project itself 302 cannot be deleted by any means. This option is set once and 303 for all (this option can not be changed after project is 304 created).""", 305 render_kw={'disabled': bool(copr)}, 306 default=False, false_values=FALSE_VALUES) 307 308 auto_prune = wtforms.BooleanField( 309 "Old builds will be deleted automatically", 310 default=True, false_values=FALSE_VALUES, 311 description="""Build will be deleted only if there is a 312 newer build (with respect to package version) and it is 313 older than 14 days""") 314 315 use_bootstrap_container = wtforms.BooleanField( 316 "Enable mock's use_bootstrap_container experimental feature", 317 description="""This will make the build slower but it has an 318 advantage that the dnf _from_ the given chroot will be used 319 to setup the chroot (otherwise host system dnf and rpm is 320 used)""", 321 default=False, 322 false_values=FALSE_VALUES) 323 324 follow_fedora_branching = wtforms.BooleanField( 325 "Follow Fedora branching", 326 description="""When Fedora is branched from rawhide, the 327 respective chroots for the new branch are automatically 328 created for you (as soon as they are available) as rawhide 329 chroot forks.""", 330 default=True, 331 false_values=FALSE_VALUES) 332 333 # Deprecated, use `enable_net` instead 334 build_enable_net = wtforms.BooleanField( 335 "Enable internet access during builds", 336 default=False, false_values=FALSE_VALUES) 337 338 enable_net = wtforms.BooleanField( 339 "Enable internet access during builds", 340 default=False, false_values=FALSE_VALUES) 341 342 @property 343 def selected_chroots(self): 344 selected = [] 345 for ch in self.chroots_list: 346 if getattr(self, ch).data: 347 selected.append(ch) 348 return selected
349 350 def validate(self): 351 if not super(F, self).validate(): 352 return False 353 354 if not self.validate_mock_chroots_not_empty(): 355 self.errors["chroots"] = ["At least one chroot must be selected"] 356 return False 357 358 if self.persistent.data and self.delete_after_days.data: 359 self.delete_after_days.errors.append( 360 "'delete after' can not be combined with persistent") 361 return False 362 363 return True
364 365 def validate_mock_chroots_not_empty(self): 366 have_any = False 367 for c in self.chroots_list: 368 if getattr(self, c).data: 369 have_any = True 370 return have_any 371 372 F.chroots_list = MockChrootsLogic.active_names() 373 F.chroots_list.sort() 374 # sets of chroots according to how we should print them in columns 375 F.chroots_sets = {} 376 for ch in F.chroots_list: 377 checkbox_default = False 378 if mock_chroots and ch in [x.name for x in mock_chroots]: 379 checkbox_default = True 380 381 setattr(F, ch, wtforms.BooleanField(ch, default=checkbox_default, false_values=FALSE_VALUES)) 382 if ch[0] in F.chroots_sets: 383 F.chroots_sets[ch[0]].append(ch) 384 else: 385 F.chroots_sets[ch[0]] = [ch] 386 387 return F 388
389 390 -class CoprDeleteForm(FlaskForm):
391 verify = wtforms.StringField( 392 "Confirm deleting by typing 'yes'", 393 validators=[ 394 wtforms.validators.DataRequired(), 395 wtforms.validators.Regexp( 396 r"^yes$", 397 message="Type 'yes' - without the quotes, lowercase.") 398 ])
399
400 401 -class APICoprDeleteForm(CoprDeleteForm):
402 verify = wtforms.BooleanField("Confirm deleting", false_values=FALSE_VALUES)
403
404 405 # @TODO jkadlcik - rewrite via BaseBuildFormFactory after fe-dev-cloud is back online 406 -class BuildFormRebuildFactory(object):
407 @staticmethod
408 - def create_form_cls(active_chroots):
409 class F(FlaskForm): 410 @property 411 def selected_chroots(self): 412 selected = [] 413 for ch in self.chroots_list: 414 if getattr(self, ch).data: 415 selected.append(ch) 416 return selected
417 418 memory_reqs = wtforms.IntegerField( 419 "Memory requirements", 420 validators=[ 421 wtforms.validators.NumberRange( 422 min=constants.MIN_BUILD_MEMORY, 423 max=constants.MAX_BUILD_MEMORY)], 424 default=constants.DEFAULT_BUILD_MEMORY) 425 426 timeout = wtforms.IntegerField( 427 "Timeout", 428 validators=[ 429 wtforms.validators.NumberRange( 430 min=constants.MIN_BUILD_TIMEOUT, 431 max=constants.MAX_BUILD_TIMEOUT)], 432 default=constants.DEFAULT_BUILD_TIMEOUT) 433 434 enable_net = wtforms.BooleanField(false_values=FALSE_VALUES) 435 background = wtforms.BooleanField(false_values=FALSE_VALUES) 436 project_dirname = wtforms.StringField(default=None)
437 438 F.chroots_list = list(map(lambda x: x.name, active_chroots)) 439 F.chroots_list.sort() 440 F.chroots_sets = {} 441 for ch in F.chroots_list: 442 setattr(F, ch, wtforms.BooleanField(ch, default=True, false_values=FALSE_VALUES)) 443 if ch[0] in F.chroots_sets: 444 F.chroots_sets[ch[0]].append(ch) 445 else: 446 F.chroots_sets[ch[0]] = [ch] 447 448 return F 449
450 451 -class RebuildPackageFactory(object):
452 @staticmethod
453 - def create_form_cls(active_chroots):
454 form = BuildFormRebuildFactory.create_form_cls(active_chroots) 455 form.package_name = wtforms.StringField( 456 "Package name", 457 validators=[wtforms.validators.DataRequired()]) 458 return form
459
460 461 -def cleanup_chroot_blacklist(string):
462 if not string: 463 return string 464 fields = [x.lstrip().rstrip() for x in string.split(',')] 465 return ', '.join(fields)
466
467 468 -def validate_chroot_blacklist(form, field):
469 if field.data: 470 string = field.data 471 fields = [x.lstrip().rstrip() for x in string.split(',')] 472 for field in fields: 473 pattern = r'^[a-z0-9-*]+$' 474 if not re.match(pattern, field): 475 raise wtforms.ValidationError('Pattern "{0}" does not match "{1}"'.format(field, pattern)) 476 477 matched = set() 478 all_chroots = MockChrootsLogic.active_names() 479 for chroot in all_chroots: 480 if fnmatch(chroot, field): 481 matched.add(chroot) 482 483 if not matched: 484 raise wtforms.ValidationError('no chroot matched by pattern "{0}"'.format(field)) 485 486 if matched == all_chroots: 487 raise wtforms.ValidationError('patterns are black-listing all chroots')
488
489 490 -class BasePackageForm(FlaskForm):
491 package_name = wtforms.StringField( 492 "Package name", 493 validators=[wtforms.validators.DataRequired()]) 494 webhook_rebuild = wtforms.BooleanField(default=False, false_values=FALSE_VALUES) 495 chroot_blacklist = wtforms.StringField( 496 "Chroot blacklist", 497 filters=[cleanup_chroot_blacklist], 498 validators=[ 499 wtforms.validators.Optional(), 500 validate_chroot_blacklist, 501 ], 502 ) 503 max_builds = wtforms.IntegerField( 504 "Max number of builds", 505 description="""Keep only the specified number of the newest-by-id builds 506 (garbage collector is run daily)""", 507 render_kw={'placeholder': 'Optional - integer, e.g. 10, zero/empty disables'}, 508 validators=[ 509 wtforms.validators.Optional(), 510 wtforms.validators.NumberRange(min=0, max=100)], 511 default=None, 512 )
513
514 515 -class PackageFormScm(BasePackageForm):
516 scm_type = wtforms.SelectField( 517 "Type", 518 choices=[("git", "Git"), ("svn", "SVN")], 519 default="git") 520 521 clone_url = wtforms.StringField( 522 "Clone url", 523 validators=[ 524 wtforms.validators.DataRequired(), 525 wtforms.validators.URL()]) 526 527 committish = wtforms.StringField( 528 "Committish", 529 validators=[ 530 wtforms.validators.Optional()]) 531 532 subdirectory = wtforms.StringField( 533 "Subdirectory", 534 validators=[ 535 wtforms.validators.Optional()]) 536 537 spec = wtforms.StringField( 538 "Spec File", 539 validators=[ 540 wtforms.validators.Optional(), 541 wtforms.validators.Regexp( 542 r"^.+\.spec$", 543 message="RPM spec file must end with .spec")]) 544 545 srpm_build_method = wtforms.SelectField( 546 "SRPM build method", 547 choices=[(x, x) for x in ["rpkg", "tito", "tito_test", "make_srpm"]], 548 default="rpkg") 549 550 @property
551 - def source_json(self):
552 return json.dumps({ 553 "type": self.scm_type.data, 554 "clone_url": self.clone_url.data, 555 "subdirectory": self.subdirectory.data, 556 "committish": self.committish.data, 557 "spec": self.spec.data, 558 "srpm_build_method": self.srpm_build_method.data, 559 })
560
561 562 -class PackageFormPyPI(BasePackageForm):
563 pypi_package_name = wtforms.StringField( 564 "PyPI package name", 565 validators=[wtforms.validators.DataRequired()]) 566 567 pypi_package_version = wtforms.StringField( 568 "PyPI package version", 569 validators=[ 570 wtforms.validators.Optional(), 571 ]) 572 573 spec_template = wtforms.SelectField( 574 "Spec template", 575 choices=[ 576 ("", "default"), 577 ("fedora", "fedora"), 578 ("epel7", "epel7"), 579 ("mageia", "mageia"), 580 ("pld", "pld"), 581 ], default="") 582 583 python_versions = MultiCheckboxField( 584 'Build for Python', 585 choices=[ 586 ('3', 'python3'), 587 ('2', 'python2') 588 ], 589 default=['3', '2']) 590 591 @property
592 - def source_json(self):
593 return json.dumps({ 594 "pypi_package_name": self.pypi_package_name.data, 595 "pypi_package_version": self.pypi_package_version.data, 596 "spec_template": self.spec_template.data, 597 "python_versions": self.python_versions.data 598 })
599
600 601 -class PackageFormRubyGems(BasePackageForm):
602 gem_name = wtforms.StringField( 603 "Gem Name", 604 validators=[wtforms.validators.DataRequired()]) 605 606 @property
607 - def source_json(self):
608 return json.dumps({ 609 "gem_name": self.gem_name.data 610 })
611
612 613 -class PackageFormTito(BasePackageForm):
614 """ 615 @deprecated 616 """ 617 git_url = wtforms.StringField( 618 "Git URL", 619 validators=[ 620 wtforms.validators.DataRequired(), 621 wtforms.validators.URL()]) 622 623 git_directory = wtforms.StringField( 624 "Git Directory", 625 validators=[ 626 wtforms.validators.Optional()]) 627 628 git_branch = wtforms.StringField( 629 "Git Branch", 630 validators=[ 631 wtforms.validators.Optional()]) 632 633 tito_test = wtforms.BooleanField(default=False, false_values=FALSE_VALUES) 634 635 @property
636 - def source_json(self):
637 return json.dumps({ 638 "type": 'git', 639 "clone_url": self.git_url.data, 640 "committish": self.git_branch.data, 641 "subdirectory": self.git_directory.data, 642 "spec": '', 643 "srpm_build_method": 'tito_test' if self.tito_test.data else 'tito', 644 })
645
646 647 -class PackageFormMock(BasePackageForm):
648 """ 649 @deprecated 650 """ 651 scm_type = wtforms.SelectField( 652 "SCM Type", 653 choices=[("git", "Git"), ("svn", "SVN")]) 654 655 scm_url = wtforms.StringField( 656 "SCM URL", 657 validators=[ 658 wtforms.validators.DataRequired(), 659 wtforms.validators.URL()]) 660 661 scm_branch = wtforms.StringField( 662 "Git Branch", 663 validators=[ 664 wtforms.validators.Optional()]) 665 666 scm_subdir = wtforms.StringField( 667 "Subdirectory", 668 validators=[ 669 wtforms.validators.Optional()]) 670 671 spec = wtforms.StringField( 672 "Spec File", 673 validators=[ 674 wtforms.validators.Optional(), 675 wtforms.validators.Regexp( 676 r"^.+\.spec$", 677 message="RPM spec file must end with .spec")]) 678 679 @property
680 - def source_json(self):
681 return json.dumps({ 682 "type": self.scm_type.data, 683 "clone_url": self.scm_url.data, 684 "committish": self.scm_branch.data, 685 "subdirectory": self.scm_subdir.data, 686 "spec": self.spec.data, 687 "srpm_build_method": 'rpkg', 688 })
689
690 691 -class PackageFormDistGit(BasePackageForm):
692 """ 693 @deprecated 694 """ 695 clone_url = wtforms.StringField( 696 "Clone Url", 697 validators=[wtforms.validators.DataRequired()]) 698 699 branch = wtforms.StringField( 700 "Branch", 701 validators=[wtforms.validators.Optional()]) 702 703 @property
704 - def source_json(self):
705 return json.dumps({ 706 "type": 'git', 707 "clone_url": self.clone_url.data, 708 "committish": self.branch.data, 709 "subdirectory": '', 710 "spec": '', 711 "srpm_build_method": 'rpkg', 712 })
713
714 715 -def cleanup_script(string):
716 if not string: 717 return string 718 719 if string.split('\n')[0].endswith('\r'): 720 # This script is most probably coming from the web-UI, where 721 # web-browsers mistakenly put '\r\n' as EOL; and that would just 722 # mean that the script is not executable (any line can mean 723 # syntax error, but namely shebang would cause 100% fail) 724 string = string.replace('\r\n', '\n') 725 726 # And append newline to have a valid unix file. 727 if not string.endswith('\n'): 728 string += '\n' 729 730 return string
731
732 733 -class PackageFormCustom(BasePackageForm):
734 script = wtforms.TextAreaField( 735 "Script", 736 validators=[ 737 wtforms.validators.DataRequired(), 738 wtforms.validators.Length( 739 max=4096, 740 message="Maximum script size is 4kB"), 741 ], 742 filters=[cleanup_script], 743 ) 744 745 builddeps = wtforms.StringField( 746 "Build dependencies", 747 validators=[wtforms.validators.Optional()]) 748 749 chroot = wtforms.SelectField( 750 'Mock chroot', 751 choices=[], 752 default='fedora-latest-x86_64', 753 ) 754 755 resultdir = wtforms.StringField( 756 "Result directory", 757 validators=[wtforms.validators.Optional()]) 758
759 - def __init__(self, *args, **kwargs):
760 super(PackageFormCustom, self).__init__(*args, **kwargs) 761 chroot_objects = models.MockChroot.query.filter(models.MockChroot.is_active).all() 762 763 chroots = [c.name for c in chroot_objects] 764 chroots.sort() 765 chroots = [(name, name) for name in chroots] 766 767 arches = set() 768 for ch in chroot_objects: 769 if ch.os_release == 'fedora': 770 arches.add(ch.arch) 771 772 self.chroot.choices = [] 773 if arches: 774 self.chroot.choices += [('fedora-latest-' + l, 'fedora-latest-' + l) for l in arches] 775 776 self.chroot.choices += chroots
777 778 @property
779 - def source_json(self):
780 return json.dumps({ 781 "script": self.script.data, 782 "chroot": self.chroot.data, 783 "builddeps": self.builddeps.data, 784 "resultdir": self.resultdir.data, 785 })
786
787 788 -class RebuildAllPackagesFormFactory(object):
789 - def __new__(cls, active_chroots, package_names):
790 form_cls = BaseBuildFormFactory(active_chroots, FlaskForm) 791 form_cls.packages = MultiCheckboxField( 792 "Packages", 793 choices=[(name, name) for name in package_names], 794 default=package_names, 795 validators=[wtforms.validators.DataRequired()]) 796 return form_cls
797
798 799 -class BaseBuildFormFactory(object):
800 - def __new__(cls, active_chroots, form, package=None):
801 class F(form): 802 @property 803 def selected_chroots(self): 804 selected = [] 805 for ch in self.chroots_list: 806 if getattr(self, ch).data: 807 selected.append(ch) 808 return selected
809 810 F.memory_reqs = wtforms.IntegerField( 811 "Memory requirements", 812 validators=[ 813 wtforms.validators.Optional(), 814 wtforms.validators.NumberRange( 815 min=constants.MIN_BUILD_MEMORY, 816 max=constants.MAX_BUILD_MEMORY)], 817 default=constants.DEFAULT_BUILD_MEMORY) 818 819 F.timeout = wtforms.IntegerField( 820 "Timeout", 821 validators=[ 822 wtforms.validators.Optional(), 823 wtforms.validators.NumberRange( 824 min=constants.MIN_BUILD_TIMEOUT, 825 max=constants.MAX_BUILD_TIMEOUT)], 826 default=constants.DEFAULT_BUILD_TIMEOUT) 827 828 F.enable_net = wtforms.BooleanField(false_values=FALSE_VALUES) 829 F.background = wtforms.BooleanField(default=False, false_values=FALSE_VALUES) 830 F.project_dirname = wtforms.StringField(default=None) 831 832 # overrides BasePackageForm.package_name and is unused for building 833 F.package_name = wtforms.StringField() 834 835 # fill chroots based on project settings 836 F.chroots_list = [x.name for x in active_chroots] 837 F.chroots_list.sort() 838 F.chroots_sets = {} 839 840 package_chroots = set(F.chroots_list) 841 if package: 842 package_chroots = set([ch.name for ch in package.chroots]) 843 844 for ch in F.chroots_list: 845 default = ch in package_chroots 846 setattr(F, ch, wtforms.BooleanField(ch, default=default, false_values=FALSE_VALUES)) 847 if ch[0] in F.chroots_sets: 848 F.chroots_sets[ch[0]].append(ch) 849 else: 850 F.chroots_sets[ch[0]] = [ch] 851 return F 852
853 854 -class BuildFormScmFactory(object):
855 - def __new__(cls, active_chroots, package=None):
857
858 859 -class BuildFormTitoFactory(object):
860 """ 861 @deprecated 862 """
863 - def __new__(cls, active_chroots):
865
866 867 -class BuildFormMockFactory(object):
868 """ 869 @deprecated 870 """
871 - def __new__(cls, active_chroots):
873
874 875 -class BuildFormPyPIFactory(object):
876 - def __new__(cls, active_chroots, package=None):
878
879 880 -class BuildFormRubyGemsFactory(object):
881 - def __new__(cls, active_chroots, package=None):
883
884 885 -class BuildFormDistGitFactory(object):
886 - def __new__(cls, active_chroots):
888
889 890 -class BuildFormUploadFactory(object):
891 - def __new__(cls, active_chroots):
892 form = BaseBuildFormFactory(active_chroots, FlaskForm) 893 form.pkgs = FileField('srpm', validators=[ 894 FileRequired(), 895 SrpmValidator()]) 896 return form
897
898 899 -class BuildFormCustomFactory(object):
900 - def __new__(cls, active_chroots, package=None):
902
903 904 -class BuildFormUrlFactory(object):
905 - def __new__(cls, active_chroots):
906 form = BaseBuildFormFactory(active_chroots, FlaskForm) 907 form.pkgs = wtforms.TextAreaField( 908 "Pkgs", 909 validators=[ 910 wtforms.validators.DataRequired(message="URLs to packages are required"), 911 UrlListValidator(), 912 UrlSrpmListValidator()], 913 filters=[StringListFilter()]) 914 return form
915
916 917 -class ModuleFormUploadFactory(FlaskForm):
918 modulemd = FileField("modulemd", validators=[ 919 FileRequired(), 920 # @TODO Validate modulemd.yaml file 921 ]) 922 923 create = wtforms.BooleanField("create", default=True, false_values=FALSE_VALUES) 924 build = wtforms.BooleanField("build", default=True, false_values=FALSE_VALUES)
925
926 927 -class ModuleBuildForm(FlaskForm):
928 modulemd = FileField("modulemd") 929 scmurl = wtforms.StringField() 930 branch = wtforms.StringField()
931
932 933 -class PagureIntegrationForm(FlaskForm):
934 repo_url = wtforms.StringField("repo_url", default='') 935 api_key = wtforms.StringField("api_key", default='') 936
937 - def __init__(self, api_key=None, repo_url=None, *args, **kwargs):
938 super(PagureIntegrationForm, self).__init__(*args, **kwargs) 939 if api_key != None: 940 self.api_key.data = api_key 941 if repo_url != None: 942 self.repo_url.data = repo_url
943
944 945 -class ChrootForm(FlaskForm):
946 947 """ 948 Validator for editing chroots in project 949 (adding packages to minimal chroot) 950 """ 951 952 buildroot_pkgs = wtforms.StringField("Packages") 953 954 repos = wtforms.TextAreaField('Repos', 955 validators=[UrlRepoListValidator(), 956 wtforms.validators.Optional()], 957 filters=[StringListFilter()]) 958 959 comps = FileField("comps_xml") 960 961 with_opts = wtforms.StringField("With options") 962 without_opts = wtforms.StringField("Without options")
963
964 965 -class CoprChrootExtend(FlaskForm):
966 extend = wtforms.StringField("Chroot name") 967 expire = wtforms.StringField("Chroot name")
968
969 970 -class CoprLegalFlagForm(FlaskForm):
971 comment = wtforms.TextAreaField("Comment")
972
973 974 -class PermissionsApplierFormFactory(object):
975 976 @staticmethod
977 - def create_form_cls(permission=None):
978 class F(FlaskForm): 979 pass
980 981 builder_default = False 982 admin_default = False 983 984 if permission: 985 if permission.copr_builder != helpers.PermissionEnum("nothing"): 986 builder_default = True 987 if permission.copr_admin != helpers.PermissionEnum("nothing"): 988 admin_default = True 989 990 setattr(F, "copr_builder", 991 wtforms.BooleanField( 992 default=builder_default, 993 false_values=FALSE_VALUES, 994 filters=[ValueToPermissionNumberFilter()])) 995 996 setattr(F, "copr_admin", 997 wtforms.BooleanField( 998 default=admin_default, 999 false_values=FALSE_VALUES, 1000 filters=[ValueToPermissionNumberFilter()])) 1001 1002 return F
1003
1004 1005 -class PermissionsFormFactory(object):
1006 1007 """Creates a dynamic form for given set of copr permissions""" 1008 @staticmethod
1009 - def create_form_cls(permissions):
1010 class F(FlaskForm): 1011 pass
1012 1013 for perm in permissions: 1014 builder_choices = helpers.PermissionEnum.choices_list() 1015 admin_choices = helpers.PermissionEnum.choices_list() 1016 1017 builder_default = perm.copr_builder 1018 admin_default = perm.copr_admin 1019 1020 setattr(F, "copr_builder_{0}".format(perm.user.id), 1021 wtforms.SelectField( 1022 choices=builder_choices, 1023 default=builder_default, 1024 coerce=int)) 1025 1026 setattr(F, "copr_admin_{0}".format(perm.user.id), 1027 wtforms.SelectField( 1028 choices=admin_choices, 1029 default=admin_default, 1030 coerce=int)) 1031 1032 return F
1033
1034 1035 -class CoprModifyForm(FlaskForm):
1036 description = wtforms.TextAreaField('Description', 1037 validators=[wtforms.validators.Optional()]) 1038 1039 instructions = wtforms.TextAreaField('Instructions', 1040 validators=[wtforms.validators.Optional()]) 1041 1042 chroots = wtforms.TextAreaField('Chroots', 1043 validators=[wtforms.validators.Optional(), ChrootsValidator()]) 1044 1045 repos = wtforms.TextAreaField('External Repositories', 1046 validators=[UrlRepoListValidator(), 1047 wtforms.validators.Optional()], 1048 filters=[StringListFilter()]) 1049 1050 disable_createrepo = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1051 unlisted_on_hp = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1052 auto_prune = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1053 use_bootstrap_container = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1054 follow_fedora_branching = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1055 follow_fedora_branching = wtforms.BooleanField(default=True, false_values=FALSE_VALUES) 1056 delete_after_days = wtforms.IntegerField( 1057 validators=[wtforms.validators.Optional(), 1058 wtforms.validators.NumberRange(min=-1, max=60)], 1059 filters=[(lambda x : -1 if x is None else x)]) 1060 1061 # Deprecated, use `enable_net` instead 1062 build_enable_net = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1063 enable_net = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES)
1064
1065 1066 -class CoprForkFormFactory(object):
1067 @staticmethod
1068 - def create_form_cls(copr, user, groups):
1069 class F(FlaskForm): 1070 source = wtforms.StringField( 1071 "Source", 1072 default=copr.full_name) 1073 1074 owner = wtforms.SelectField( 1075 "Fork owner", 1076 choices=[(user.name, user.name)] + [(g.at_name, g.at_name) for g in groups], 1077 default=user.name, 1078 validators=[wtforms.validators.DataRequired()]) 1079 1080 name = wtforms.StringField( 1081 "Fork name", 1082 default=copr.name, 1083 validators=[wtforms.validators.DataRequired(), NameCharactersValidator()]) 1084 1085 confirm = wtforms.BooleanField( 1086 "Confirm", 1087 false_values=FALSE_VALUES, 1088 default=False)
1089 return F
1090
1091 1092 -class ModifyChrootForm(ChrootForm):
1093 buildroot_pkgs = wtforms.StringField('Additional packages to be always present in minimal buildroot') 1094 repos = wtforms.TextAreaField('Additional repos to be used for builds in chroot', 1095 validators=[UrlRepoListValidator(), 1096 wtforms.validators.Optional()], 1097 filters=[StringListFilter()]) 1098 comps = None 1099 upload_comps = FileField("Upload comps.xml") 1100 delete_comps = wtforms.BooleanField("Delete comps.xml", false_values=FALSE_VALUES)
1101
1102 1103 -class PinnedCoprsForm(FlaskForm):
1104 copr_ids = wtforms.SelectMultipleField(wtforms.IntegerField("Pinned Copr ID")) 1105
1106 - def validate(self):
1107 if any([i and not i.isnumeric() for i in self.copr_ids.data]): 1108 self.errors["coprs"] = ["Unexpected value selected"] 1109 return False 1110 1111 limit = app.config["PINNED_PROJECTS_LIMIT"] 1112 if len(self.copr_ids.data) > limit: 1113 self.errors["coprs"] = ["Too many pinned projects. Limit is {}!".format(limit)] 1114 return False 1115 1116 if len(list(filter(None, self.copr_ids.data))) != len(set(filter(None, self.copr_ids.data))): 1117 self.errors["coprs"] = ["You can pin a particular project only once"] 1118 return False 1119 1120 return True
1121
1122 1123 -class AdminPlaygroundForm(FlaskForm):
1124 playground = wtforms.BooleanField("Playground", false_values=FALSE_VALUES)
1125
1126 1127 -class AdminPlaygroundSearchForm(FlaskForm):
1128 project = wtforms.StringField("Project")
1129
1130 1131 -class GroupUniqueNameValidator(object):
1132
1133 - def __init__(self, message=None):
1134 if not message: 1135 message = "Group with the alias '{}' already exists." 1136 self.message = message
1137
1138 - def __call__(self, form, field):
1139 if UsersLogic.group_alias_exists(field.data): 1140 raise wtforms.ValidationError(self.message.format(field.data))
1141
1142 1143 -class ActivateFasGroupForm(FlaskForm):
1144 1145 name = wtforms.StringField( 1146 validators=[ 1147 wtforms.validators.Regexp( 1148 re.compile(r"^[\w.-]+$"), 1149 message="Name must contain only letters," 1150 "digits, underscores, dashes and dots."), 1151 GroupUniqueNameValidator() 1152 ] 1153 )
1154
1155 1156 -class CreateModuleForm(FlaskForm):
1157 builds = wtforms.FieldList(wtforms.StringField("Builds ID list")) 1158 packages = wtforms.FieldList(wtforms.StringField("Packages list")) 1159 filter = wtforms.FieldList(wtforms.StringField("Package Filter")) 1160 api = wtforms.FieldList(wtforms.StringField("Module API")) 1161 profile_names = wtforms.FieldList(wtforms.StringField("Install Profiles"), min_entries=2) 1162 profile_pkgs = wtforms.FieldList(wtforms.FieldList(wtforms.StringField("Install Profiles")), min_entries=2) 1163
1164 - def __init__(self, copr=None, *args, **kwargs):
1165 self.copr = copr 1166 super(CreateModuleForm, self).__init__(*args, **kwargs)
1167
1168 - def validate(self):
1169 if not FlaskForm.validate(self): 1170 return False 1171 1172 # Profile names should be unique 1173 names = [x for x in self.profile_names.data if x] 1174 if len(set(names)) < len(names): 1175 self.errors["profiles"] = ["Profile names must be unique"] 1176 return False 1177 1178 # WORKAROUND 1179 # profile_pkgs are somehow sorted so if I fill profile_name in the first box and 1180 # profile_pkgs in seconds box, it is sorted and validated correctly 1181 for i in range(0, len(self.profile_names.data)): 1182 # If profile name is not set, then there should not be any packages in this profile 1183 if not flask.request.form["profile_names-{}".format(i)]: 1184 if [j for j in range(0, len(self.profile_names)) if "profile_pkgs-{}-{}".format(i, j) in flask.request.form]: 1185 self.errors["profiles"] = ["Missing profile name"] 1186 return False 1187 return True
1188
1189 1190 -class ModuleRepo(FlaskForm):
1191 owner = wtforms.StringField("Owner Name", validators=[wtforms.validators.DataRequired()]) 1192 copr = wtforms.StringField("Copr Name", validators=[wtforms.validators.DataRequired()]) 1193 name = wtforms.StringField("Name", validators=[wtforms.validators.DataRequired()]) 1194 stream = wtforms.StringField("Stream", validators=[wtforms.validators.DataRequired()]) 1195 version = wtforms.IntegerField("Version", validators=[wtforms.validators.DataRequired()]) 1196 arch = wtforms.StringField("Arch", validators=[wtforms.validators.DataRequired()])
1197