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 helpers 
  18  from coprs import models 
  19  from coprs.logic.coprs_logic import CoprsLogic, MockChrootsLogic 
  20  from coprs.logic.users_logic import UsersLogic 
  21  from coprs import exceptions 
  22   
  23   
  24  FALSE_VALUES = {False, "false", ""} 
25 26 27 -def get_package_form_cls_by_source_type_text(source_type_text):
28 """ 29 Params 30 ------ 31 source_type_text : str 32 name of the source type (scm/pypi/rubygems/git_and_tito/mock_scm) 33 34 Returns 35 ------- 36 BasePackageForm child 37 based on source_type_text input 38 """ 39 if source_type_text == 'scm': 40 return PackageFormScm 41 elif source_type_text == 'pypi': 42 return PackageFormPyPI 43 elif source_type_text == 'rubygems': 44 return PackageFormRubyGems 45 elif source_type_text == 'git_and_tito': 46 return PackageFormTito # deprecated 47 elif source_type_text == 'mock_scm': 48 return PackageFormMock # deprecated 49 elif source_type_text == "custom": 50 return PackageFormCustom 51 else: 52 raise exceptions.UnknownSourceTypeException("Invalid source type")
53
54 55 -class MultiCheckboxField(wtforms.SelectMultipleField):
56 widget = wtforms.widgets.ListWidget(prefix_label=False) 57 option_widget = wtforms.widgets.CheckboxInput()
58
59 60 -class UrlListValidator(object):
61
62 - def __init__(self, message=None):
63 if not message: 64 message = ("A list of http[s] URLs separated by whitespace characters" 65 " is needed ('{0}' doesn't seem to be a valid URL).") 66 self.message = message
67
68 - def __call__(self, form, field):
69 urls = field.data.split() 70 for u in urls: 71 if not self.is_url(u): 72 raise wtforms.ValidationError(self.message.format(u))
73
74 - def is_url(self, url):
75 parsed = urlparse(url) 76 if not parsed.scheme.startswith("http"): 77 return False 78 if not parsed.netloc: 79 return False 80 return True
81
82 83 -class UrlRepoListValidator(UrlListValidator):
84 """ Allows also `repo://` schema"""
85 - def is_url(self, url):
86 parsed = urlparse(url) 87 if parsed.scheme not in ["http", "https", "copr"]: 88 return False 89 if not parsed.netloc: 90 return False 91 # copr://username/projectname 92 # ^^ schema ^^ netlock ^^ path 93 if parsed.scheme == "copr": 94 # check if projectname missed 95 path_split = parsed.path.split("/") 96 if len(path_split) < 2 or path_split[1] == "": 97 return False 98 99 return True
100
101 102 -class UrlSrpmListValidator(UrlListValidator):
103 - def __init__(self, message=None):
104 if not message: 105 message = ("URLs must end with .src.rpm, .nosrc.rpm, or .spec" 106 " ('{0}' doesn't seem to be a valid URL).") 107 super(UrlSrpmListValidator, self).__init__(message)
108
109 - def is_url(self, url):
110 parsed = urlparse(url) 111 if not parsed.path.endswith((".src.rpm", ".nosrc.rpm", ".spec")): 112 return False 113 return True
114
115 116 -class SrpmValidator(object):
117 - def __init__(self, message=None):
118 if not message: 119 message = "You can upload only .src.rpm, .nosrc.rpm, and .spec files" 120 self.message = message
121
122 - def __call__(self, form, field):
123 filename = field.data.filename.lower() 124 if not filename.endswith((".src.rpm", ".nosrc.rpm", ".spec")): 125 raise wtforms.ValidationError(self.message)
126
127 128 -class CoprUniqueNameValidator(object):
129
130 - def __init__(self, message=None, user=None, group=None):
131 if not message: 132 if group is None: 133 message = "You already have project named '{}'." 134 else: 135 message = "Group {} ".format(group) + "already have project named '{}'." 136 self.message = message 137 if not user: 138 user = flask.g.user 139 self.user = user 140 self.group = group
141
142 - def __call__(self, form, field):
143 if self.group: 144 existing = CoprsLogic.exists_for_group( 145 self.group, field.data).first() 146 else: 147 existing = CoprsLogic.exists_for_user( 148 self.user, field.data).first() 149 150 if existing and str(existing.id) != form.id.data: 151 raise wtforms.ValidationError(self.message.format(field.data))
152
153 154 -class NameCharactersValidator(object):
155 - def __init__(self, message=None):
156 if not message: 157 message = "Name must contain only letters, digits, underscores, dashes and dots." 158 self.message = message
159
160 - def __call__(self, form, field):
161 validator = wtforms.validators.Regexp( 162 re.compile(r"^[\w.-]+$"), 163 message=self.message) 164 validator(form, field)
165
166 167 -class ChrootsValidator(object):
168 - def __call__(self, form, field):
169 # Allow it to be truly optional and has None value 170 if not field.data: 171 return 172 173 selected = set(field.data.split()) 174 enabled = set(MockChrootsLogic.active_names()) 175 176 if selected - enabled: 177 raise wtforms.ValidationError("Such chroot is not available: {}".format(", ".join(selected - enabled)))
178
179 180 -class NameNotNumberValidator(object):
181
182 - def __init__(self, message=None):
183 if not message: 184 message = "Project's name can not be just number." 185 self.message = message
186
187 - def __call__(self, form, field):
188 if field.data.isdigit(): 189 raise wtforms.ValidationError(self.message.format(field.data))
190
191 192 -class EmailOrURL(object):
193
194 - def __init__(self, message=None):
195 if not message: 196 message = "{} must be email address or URL" 197 self.message = message
198
199 - def __call__(self, form, field):
200 for validator in [wtforms.validators.Email(), wtforms.validators.URL()]: 201 try: 202 validator(form, field) 203 return True 204 except wtforms.ValidationError: 205 pass 206 raise wtforms.ValidationError(self.message.format(field.name.capitalize()))
207
208 209 -class StringListFilter(object):
210
211 - def __call__(self, value):
212 if not value: 213 return '' 214 # Replace every whitespace string with one newline 215 # Formats ideally for html form filling, use replace('\n', ' ') 216 # to get space-separated values or split() to get list 217 result = value.strip() 218 regex = re.compile(r"\s+") 219 return regex.sub(lambda x: '\n', result)
220
221 222 -class ValueToPermissionNumberFilter(object):
223
224 - def __call__(self, value):
225 if value: 226 return helpers.PermissionEnum("request") 227 return helpers.PermissionEnum("nothing")
228
229 230 -class CoprFormFactory(object):
231 232 @staticmethod
233 - def create_form_cls(mock_chroots=None, user=None, group=None, copr=None):
234 class F(FlaskForm): 235 # also use id here, to be able to find out whether user 236 # is updating a copr if so, we don't want to shout 237 # that name already exists 238 id = wtforms.HiddenField() 239 group_id = wtforms.HiddenField() 240 241 name = wtforms.StringField( 242 "Name", 243 validators=[ 244 wtforms.validators.DataRequired(), 245 NameCharactersValidator(), 246 CoprUniqueNameValidator(user=user, group=group), 247 NameNotNumberValidator() 248 ]) 249 250 homepage = wtforms.StringField( 251 "Homepage", 252 validators=[ 253 wtforms.validators.Optional(), 254 wtforms.validators.URL()]) 255 256 contact = wtforms.StringField( 257 "Contact", 258 validators=[ 259 wtforms.validators.Optional(), 260 EmailOrURL()]) 261 262 description = wtforms.TextAreaField("Description") 263 264 instructions = wtforms.TextAreaField("Instructions") 265 266 delete_after_days = wtforms.IntegerField( 267 "Delete after days", 268 validators=[ 269 wtforms.validators.Optional(), 270 wtforms.validators.NumberRange(min=0, max=60), 271 ], 272 render_kw={'disabled': bool(copr and copr.persistent)}) 273 274 repos = wtforms.TextAreaField( 275 "External Repositories", 276 validators=[UrlRepoListValidator()], 277 filters=[StringListFilter()]) 278 279 initial_pkgs = wtforms.TextAreaField( 280 "Initial packages to build", 281 validators=[ 282 UrlListValidator(), 283 UrlSrpmListValidator()], 284 filters=[StringListFilter()]) 285 286 disable_createrepo = wtforms.BooleanField(default=False, 287 label="Create repositories manually", 288 description="""When Fedora is branched from rawhide, the 289 respective chroots for the new branch are automatically 290 created for you (as soon as they are available) as rawhide 291 chroot forks.""", 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.TextField( 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.TextField("Packages") 953 954 repos = wtforms.TextAreaField('Repos', 955 validators=[UrlRepoListValidator(), 956 wtforms.validators.Optional()], 957 filters=[StringListFilter()]) 958 959 module_md = FileField("module_md") 960 961 comps = FileField("comps_xml") 962 963 with_opts = wtforms.TextField("With options") 964 without_opts = wtforms.TextField("Without options")
965
966 967 -class CoprChrootExtend(FlaskForm):
968 extend = wtforms.StringField("Chroot name") 969 expire = wtforms.StringField("Chroot name")
970
971 972 -class CoprLegalFlagForm(FlaskForm):
973 comment = wtforms.TextAreaField("Comment")
974
975 976 -class PermissionsApplierFormFactory(object):
977 978 @staticmethod
979 - def create_form_cls(permission=None):
980 class F(FlaskForm): 981 pass
982 983 builder_default = False 984 admin_default = False 985 986 if permission: 987 if permission.copr_builder != helpers.PermissionEnum("nothing"): 988 builder_default = True 989 if permission.copr_admin != helpers.PermissionEnum("nothing"): 990 admin_default = True 991 992 setattr(F, "copr_builder", 993 wtforms.BooleanField( 994 default=builder_default, 995 false_values=FALSE_VALUES, 996 filters=[ValueToPermissionNumberFilter()])) 997 998 setattr(F, "copr_admin", 999 wtforms.BooleanField( 1000 default=admin_default, 1001 false_values=FALSE_VALUES, 1002 filters=[ValueToPermissionNumberFilter()])) 1003 1004 return F
1005
1006 1007 -class PermissionsFormFactory(object):
1008 1009 """Creates a dynamic form for given set of copr permissions""" 1010 @staticmethod
1011 - def create_form_cls(permissions):
1012 class F(FlaskForm): 1013 pass
1014 1015 for perm in permissions: 1016 builder_choices = helpers.PermissionEnum.choices_list() 1017 admin_choices = helpers.PermissionEnum.choices_list() 1018 1019 builder_default = perm.copr_builder 1020 admin_default = perm.copr_admin 1021 1022 setattr(F, "copr_builder_{0}".format(perm.user.id), 1023 wtforms.SelectField( 1024 choices=builder_choices, 1025 default=builder_default, 1026 coerce=int)) 1027 1028 setattr(F, "copr_admin_{0}".format(perm.user.id), 1029 wtforms.SelectField( 1030 choices=admin_choices, 1031 default=admin_default, 1032 coerce=int)) 1033 1034 return F
1035
1036 1037 -class CoprModifyForm(FlaskForm):
1038 description = wtforms.TextAreaField('Description', 1039 validators=[wtforms.validators.Optional()]) 1040 1041 instructions = wtforms.TextAreaField('Instructions', 1042 validators=[wtforms.validators.Optional()]) 1043 1044 chroots = wtforms.TextAreaField('Chroots', 1045 validators=[wtforms.validators.Optional(), ChrootsValidator()]) 1046 1047 repos = wtforms.TextAreaField('Repos', 1048 validators=[UrlRepoListValidator(), 1049 wtforms.validators.Optional()], 1050 filters=[StringListFilter()]) 1051 1052 disable_createrepo = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1053 unlisted_on_hp = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1054 auto_prune = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1055 use_bootstrap_container = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1056 follow_fedora_branching = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1057 follow_fedora_branching = wtforms.BooleanField(default=True, false_values=FALSE_VALUES) 1058 delete_after_days = wtforms.IntegerField( 1059 validators=[wtforms.validators.Optional(), 1060 wtforms.validators.NumberRange(min=-1, max=60)], 1061 filters=[(lambda x : -1 if x is None else x)]) 1062 1063 # Deprecated, use `enable_net` instead 1064 build_enable_net = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1065 enable_net = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES)
1066
1067 1068 -class CoprForkFormFactory(object):
1069 @staticmethod
1070 - def create_form_cls(copr, user, groups):
1071 class F(FlaskForm): 1072 source = wtforms.StringField( 1073 "Source", 1074 default=copr.full_name) 1075 1076 owner = wtforms.SelectField( 1077 "Fork owner", 1078 choices=[(user.name, user.name)] + [(g.at_name, g.at_name) for g in groups], 1079 default=user.name, 1080 validators=[wtforms.validators.DataRequired()]) 1081 1082 name = wtforms.StringField( 1083 "Fork name", 1084 default=copr.name, 1085 validators=[wtforms.validators.DataRequired(), NameCharactersValidator()]) 1086 1087 confirm = wtforms.BooleanField( 1088 "Confirm", 1089 false_values=FALSE_VALUES, 1090 default=False)
1091 return F
1092
1093 1094 -class ModifyChrootForm(ChrootForm):
1095 buildroot_pkgs = wtforms.TextField('Additional packages to be always present in minimal buildroot') 1096 repos = wtforms.TextAreaField('Additional repos to be used for builds in chroot', 1097 validators=[UrlRepoListValidator(), 1098 wtforms.validators.Optional()], 1099 filters=[StringListFilter()]) 1100 comps = None 1101 upload_comps = FileField("Upload comps.xml") 1102 delete_comps = wtforms.BooleanField("Delete comps.xml", false_values=FALSE_VALUES)
1103
1104 -class AdminPlaygroundForm(FlaskForm):
1105 playground = wtforms.BooleanField("Playground", false_values=FALSE_VALUES)
1106
1107 1108 -class AdminPlaygroundSearchForm(FlaskForm):
1109 project = wtforms.TextField("Project")
1110
1111 1112 -class GroupUniqueNameValidator(object):
1113
1114 - def __init__(self, message=None):
1115 if not message: 1116 message = "Group with the alias '{}' already exists." 1117 self.message = message
1118
1119 - def __call__(self, form, field):
1120 if UsersLogic.group_alias_exists(field.data): 1121 raise wtforms.ValidationError(self.message.format(field.data))
1122
1123 1124 -class ActivateFasGroupForm(FlaskForm):
1125 1126 name = wtforms.StringField( 1127 validators=[ 1128 wtforms.validators.Regexp( 1129 re.compile(r"^[\w.-]+$"), 1130 message="Name must contain only letters," 1131 "digits, underscores, dashes and dots."), 1132 GroupUniqueNameValidator() 1133 ] 1134 )
1135
1136 1137 -class CreateModuleForm(FlaskForm):
1138 builds = wtforms.FieldList(wtforms.StringField("Builds ID list")) 1139 packages = wtforms.FieldList(wtforms.StringField("Packages list")) 1140 filter = wtforms.FieldList(wtforms.StringField("Package Filter")) 1141 api = wtforms.FieldList(wtforms.StringField("Module API")) 1142 profile_names = wtforms.FieldList(wtforms.StringField("Install Profiles"), min_entries=2) 1143 profile_pkgs = wtforms.FieldList(wtforms.FieldList(wtforms.StringField("Install Profiles")), min_entries=2) 1144
1145 - def __init__(self, copr=None, *args, **kwargs):
1146 self.copr = copr 1147 super(CreateModuleForm, self).__init__(*args, **kwargs)
1148
1149 - def validate(self):
1150 if not FlaskForm.validate(self): 1151 return False 1152 1153 # Profile names should be unique 1154 names = [x for x in self.profile_names.data if x] 1155 if len(set(names)) < len(names): 1156 self.errors["profiles"] = ["Profile names must be unique"] 1157 return False 1158 1159 # WORKAROUND 1160 # profile_pkgs are somehow sorted so if I fill profile_name in the first box and 1161 # profile_pkgs in seconds box, it is sorted and validated correctly 1162 for i in range(0, len(self.profile_names.data)): 1163 # If profile name is not set, then there should not be any packages in this profile 1164 if not flask.request.form["profile_names-{}".format(i)]: 1165 if [j for j in range(0, len(self.profile_names)) if "profile_pkgs-{}-{}".format(i, j) in flask.request.form]: 1166 self.errors["profiles"] = ["Missing profile name"] 1167 return False 1168 return True
1169
1170 1171 -class ModuleRepo(FlaskForm):
1172 owner = wtforms.StringField("Owner Name", validators=[wtforms.validators.DataRequired()]) 1173 copr = wtforms.StringField("Copr Name", validators=[wtforms.validators.DataRequired()]) 1174 name = wtforms.StringField("Name", validators=[wtforms.validators.DataRequired()]) 1175 stream = wtforms.StringField("Stream", validators=[wtforms.validators.DataRequired()]) 1176 version = wtforms.IntegerField("Version", validators=[wtforms.validators.DataRequired()]) 1177 arch = wtforms.StringField("Arch", validators=[wtforms.validators.DataRequired()])
1178