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="""Repository meta data is normally refreshed 289 after each build. If you want to do this manually, turn 290 this option on.""", 291 false_values=FALSE_VALUES) 292 293 unlisted_on_hp = wtforms.BooleanField( 294 "Project will not be listed on home page", 295 default=False, 296 false_values=FALSE_VALUES) 297 298 persistent = wtforms.BooleanField( 299 "Protect project and its builds against deletion", 300 description="""Project's builds and the project itself 301 cannot be deleted by any means. This option is set once and 302 for all (this option can not be changed after project is 303 created).""", 304 render_kw={'disabled': bool(copr)}, 305 default=False, false_values=FALSE_VALUES) 306 307 auto_prune = wtforms.BooleanField( 308 "Old builds will be deleted automatically", 309 default=True, false_values=FALSE_VALUES, 310 description="""Build will be deleted only if there is a 311 newer build (with respect to package version) and it is 312 older than 14 days""") 313 314 use_bootstrap_container = wtforms.BooleanField( 315 "Enable mock's use_bootstrap_container experimental feature", 316 description="""This will make the build slower but it has an 317 advantage that the dnf _from_ the given chroot will be used 318 to setup the chroot (otherwise host system dnf and rpm is 319 used)""", 320 default=False, 321 false_values=FALSE_VALUES) 322 323 follow_fedora_branching = wtforms.BooleanField( 324 "Follow Fedora branching", 325 description="""When Fedora is branched from rawhide, the 326 respective chroots for the new branch are automatically 327 created for you (as soon as they are available) as rawhide 328 chroot forks.""", 329 default=True, 330 false_values=FALSE_VALUES) 331 332 # Deprecated, use `enable_net` instead 333 build_enable_net = wtforms.BooleanField( 334 "Enable internet access during builds", 335 default=False, false_values=FALSE_VALUES) 336 337 enable_net = wtforms.BooleanField( 338 "Enable internet access during builds", 339 default=False, false_values=FALSE_VALUES) 340 341 @property 342 def selected_chroots(self): 343 selected = [] 344 for ch in self.chroots_list: 345 if getattr(self, ch).data: 346 selected.append(ch) 347 return selected
348 349 def validate(self): 350 if not super(F, self).validate(): 351 return False 352 353 if not self.validate_mock_chroots_not_empty(): 354 self.errors["chroots"] = ["At least one chroot must be selected"] 355 return False 356 357 if self.persistent.data and self.delete_after_days.data: 358 self.delete_after_days.errors.append( 359 "'delete after' can not be combined with persistent") 360 return False 361 362 return True
363 364 def validate_mock_chroots_not_empty(self): 365 have_any = False 366 for c in self.chroots_list: 367 if getattr(self, c).data: 368 have_any = True 369 return have_any 370 371 F.chroots_list = MockChrootsLogic.active_names() 372 F.chroots_list.sort() 373 # sets of chroots according to how we should print them in columns 374 F.chroots_sets = {} 375 for ch in F.chroots_list: 376 checkbox_default = False 377 if mock_chroots and ch in [x.name for x in mock_chroots]: 378 checkbox_default = True 379 380 setattr(F, ch, wtforms.BooleanField(ch, default=checkbox_default, false_values=FALSE_VALUES)) 381 if ch[0] in F.chroots_sets: 382 F.chroots_sets[ch[0]].append(ch) 383 else: 384 F.chroots_sets[ch[0]] = [ch] 385 386 return F 387
388 389 -class CoprDeleteForm(FlaskForm):
390 verify = wtforms.TextField( 391 "Confirm deleting by typing 'yes'", 392 validators=[ 393 wtforms.validators.DataRequired(), 394 wtforms.validators.Regexp( 395 r"^yes$", 396 message="Type 'yes' - without the quotes, lowercase.") 397 ])
398
399 400 -class APICoprDeleteForm(CoprDeleteForm):
401 verify = wtforms.BooleanField("Confirm deleting", false_values=FALSE_VALUES)
402
403 404 # @TODO jkadlcik - rewrite via BaseBuildFormFactory after fe-dev-cloud is back online 405 -class BuildFormRebuildFactory(object):
406 @staticmethod
407 - def create_form_cls(active_chroots):
408 class F(FlaskForm): 409 @property 410 def selected_chroots(self): 411 selected = [] 412 for ch in self.chroots_list: 413 if getattr(self, ch).data: 414 selected.append(ch) 415 return selected
416 417 memory_reqs = wtforms.IntegerField( 418 "Memory requirements", 419 validators=[ 420 wtforms.validators.NumberRange( 421 min=constants.MIN_BUILD_MEMORY, 422 max=constants.MAX_BUILD_MEMORY)], 423 default=constants.DEFAULT_BUILD_MEMORY) 424 425 timeout = wtforms.IntegerField( 426 "Timeout", 427 validators=[ 428 wtforms.validators.NumberRange( 429 min=constants.MIN_BUILD_TIMEOUT, 430 max=constants.MAX_BUILD_TIMEOUT)], 431 default=constants.DEFAULT_BUILD_TIMEOUT) 432 433 enable_net = wtforms.BooleanField(false_values=FALSE_VALUES) 434 background = wtforms.BooleanField(false_values=FALSE_VALUES) 435 project_dirname = wtforms.StringField(default=None)
436 437 F.chroots_list = list(map(lambda x: x.name, active_chroots)) 438 F.chroots_list.sort() 439 F.chroots_sets = {} 440 for ch in F.chroots_list: 441 setattr(F, ch, wtforms.BooleanField(ch, default=True, false_values=FALSE_VALUES)) 442 if ch[0] in F.chroots_sets: 443 F.chroots_sets[ch[0]].append(ch) 444 else: 445 F.chroots_sets[ch[0]] = [ch] 446 447 return F 448
449 450 -class RebuildPackageFactory(object):
451 @staticmethod
452 - def create_form_cls(active_chroots):
453 form = BuildFormRebuildFactory.create_form_cls(active_chroots) 454 form.package_name = wtforms.StringField( 455 "Package name", 456 validators=[wtforms.validators.DataRequired()]) 457 return form
458
459 460 -def cleanup_chroot_blacklist(string):
461 if not string: 462 return string 463 fields = [x.lstrip().rstrip() for x in string.split(',')] 464 return ', '.join(fields)
465
466 467 -def validate_chroot_blacklist(form, field):
468 if field.data: 469 string = field.data 470 fields = [x.lstrip().rstrip() for x in string.split(',')] 471 for field in fields: 472 pattern = r'^[a-z0-9-*]+$' 473 if not re.match(pattern, field): 474 raise wtforms.ValidationError('Pattern "{0}" does not match "{1}"'.format(field, pattern)) 475 476 matched = set() 477 all_chroots = MockChrootsLogic.active_names() 478 for chroot in all_chroots: 479 if fnmatch(chroot, field): 480 matched.add(chroot) 481 482 if not matched: 483 raise wtforms.ValidationError('no chroot matched by pattern "{0}"'.format(field)) 484 485 if matched == all_chroots: 486 raise wtforms.ValidationError('patterns are black-listing all chroots')
487
488 489 -class BasePackageForm(FlaskForm):
490 package_name = wtforms.StringField( 491 "Package name", 492 validators=[wtforms.validators.DataRequired()]) 493 webhook_rebuild = wtforms.BooleanField(default=False, false_values=FALSE_VALUES) 494 chroot_blacklist = wtforms.StringField( 495 "Chroot blacklist", 496 filters=[cleanup_chroot_blacklist], 497 validators=[ 498 wtforms.validators.Optional(), 499 validate_chroot_blacklist, 500 ], 501 ) 502 max_builds = wtforms.IntegerField( 503 "Max number of builds", 504 description="""Keep only the specified number of the newest-by-id builds 505 (garbage collector is run daily)""", 506 render_kw={'placeholder': 'Optional - integer, e.g. 10, zero/empty disables'}, 507 validators=[ 508 wtforms.validators.Optional(), 509 wtforms.validators.NumberRange(min=0, max=100)], 510 default=None, 511 )
512
513 514 -class PackageFormScm(BasePackageForm):
515 scm_type = wtforms.SelectField( 516 "Type", 517 choices=[("git", "Git"), ("svn", "SVN")], 518 default="git") 519 520 clone_url = wtforms.StringField( 521 "Clone url", 522 validators=[ 523 wtforms.validators.DataRequired(), 524 wtforms.validators.URL()]) 525 526 committish = wtforms.StringField( 527 "Committish", 528 validators=[ 529 wtforms.validators.Optional()]) 530 531 subdirectory = wtforms.StringField( 532 "Subdirectory", 533 validators=[ 534 wtforms.validators.Optional()]) 535 536 spec = wtforms.StringField( 537 "Spec File", 538 validators=[ 539 wtforms.validators.Optional(), 540 wtforms.validators.Regexp( 541 r"^.+\.spec$", 542 message="RPM spec file must end with .spec")]) 543 544 srpm_build_method = wtforms.SelectField( 545 "SRPM build method", 546 choices=[(x, x) for x in ["rpkg", "tito", "tito_test", "make_srpm"]], 547 default="rpkg") 548 549 @property
550 - def source_json(self):
551 return json.dumps({ 552 "type": self.scm_type.data, 553 "clone_url": self.clone_url.data, 554 "subdirectory": self.subdirectory.data, 555 "committish": self.committish.data, 556 "spec": self.spec.data, 557 "srpm_build_method": self.srpm_build_method.data, 558 })
559
560 561 -class PackageFormPyPI(BasePackageForm):
562 pypi_package_name = wtforms.StringField( 563 "PyPI package name", 564 validators=[wtforms.validators.DataRequired()]) 565 566 pypi_package_version = wtforms.StringField( 567 "PyPI package version", 568 validators=[ 569 wtforms.validators.Optional(), 570 ]) 571 572 spec_template = wtforms.SelectField( 573 "Spec template", 574 choices=[ 575 ("", "default"), 576 ("fedora", "fedora"), 577 ("epel7", "epel7"), 578 ("mageia", "mageia"), 579 ("pld", "pld"), 580 ], default="") 581 582 python_versions = MultiCheckboxField( 583 'Build for Python', 584 choices=[ 585 ('3', 'python3'), 586 ('2', 'python2') 587 ], 588 default=['3', '2']) 589 590 @property
591 - def source_json(self):
592 return json.dumps({ 593 "pypi_package_name": self.pypi_package_name.data, 594 "pypi_package_version": self.pypi_package_version.data, 595 "spec_template": self.spec_template.data, 596 "python_versions": self.python_versions.data 597 })
598
599 600 -class PackageFormRubyGems(BasePackageForm):
601 gem_name = wtforms.StringField( 602 "Gem Name", 603 validators=[wtforms.validators.DataRequired()]) 604 605 @property
606 - def source_json(self):
607 return json.dumps({ 608 "gem_name": self.gem_name.data 609 })
610
611 612 -class PackageFormTito(BasePackageForm):
613 """ 614 @deprecated 615 """ 616 git_url = wtforms.StringField( 617 "Git URL", 618 validators=[ 619 wtforms.validators.DataRequired(), 620 wtforms.validators.URL()]) 621 622 git_directory = wtforms.StringField( 623 "Git Directory", 624 validators=[ 625 wtforms.validators.Optional()]) 626 627 git_branch = wtforms.StringField( 628 "Git Branch", 629 validators=[ 630 wtforms.validators.Optional()]) 631 632 tito_test = wtforms.BooleanField(default=False, false_values=FALSE_VALUES) 633 634 @property
635 - def source_json(self):
636 return json.dumps({ 637 "type": 'git', 638 "clone_url": self.git_url.data, 639 "committish": self.git_branch.data, 640 "subdirectory": self.git_directory.data, 641 "spec": '', 642 "srpm_build_method": 'tito_test' if self.tito_test.data else 'tito', 643 })
644
645 646 -class PackageFormMock(BasePackageForm):
647 """ 648 @deprecated 649 """ 650 scm_type = wtforms.SelectField( 651 "SCM Type", 652 choices=[("git", "Git"), ("svn", "SVN")]) 653 654 scm_url = wtforms.StringField( 655 "SCM URL", 656 validators=[ 657 wtforms.validators.DataRequired(), 658 wtforms.validators.URL()]) 659 660 scm_branch = wtforms.StringField( 661 "Git Branch", 662 validators=[ 663 wtforms.validators.Optional()]) 664 665 scm_subdir = wtforms.StringField( 666 "Subdirectory", 667 validators=[ 668 wtforms.validators.Optional()]) 669 670 spec = wtforms.StringField( 671 "Spec File", 672 validators=[ 673 wtforms.validators.Optional(), 674 wtforms.validators.Regexp( 675 r"^.+\.spec$", 676 message="RPM spec file must end with .spec")]) 677 678 @property
679 - def source_json(self):
680 return json.dumps({ 681 "type": self.scm_type.data, 682 "clone_url": self.scm_url.data, 683 "committish": self.scm_branch.data, 684 "subdirectory": self.scm_subdir.data, 685 "spec": self.spec.data, 686 "srpm_build_method": 'rpkg', 687 })
688
689 690 -class PackageFormDistGit(BasePackageForm):
691 """ 692 @deprecated 693 """ 694 clone_url = wtforms.StringField( 695 "Clone Url", 696 validators=[wtforms.validators.DataRequired()]) 697 698 branch = wtforms.StringField( 699 "Branch", 700 validators=[wtforms.validators.Optional()]) 701 702 @property
703 - def source_json(self):
704 return json.dumps({ 705 "type": 'git', 706 "clone_url": self.clone_url.data, 707 "committish": self.branch.data, 708 "subdirectory": '', 709 "spec": '', 710 "srpm_build_method": 'rpkg', 711 })
712
713 714 -def cleanup_script(string):
715 if not string: 716 return string 717 718 if string.split('\n')[0].endswith('\r'): 719 # This script is most probably coming from the web-UI, where 720 # web-browsers mistakenly put '\r\n' as EOL; and that would just 721 # mean that the script is not executable (any line can mean 722 # syntax error, but namely shebang would cause 100% fail) 723 string = string.replace('\r\n', '\n') 724 725 # And append newline to have a valid unix file. 726 if not string.endswith('\n'): 727 string += '\n' 728 729 return string
730
731 732 -class PackageFormCustom(BasePackageForm):
733 script = wtforms.TextAreaField( 734 "Script", 735 validators=[ 736 wtforms.validators.DataRequired(), 737 wtforms.validators.Length( 738 max=4096, 739 message="Maximum script size is 4kB"), 740 ], 741 filters=[cleanup_script], 742 ) 743 744 builddeps = wtforms.StringField( 745 "Build dependencies", 746 validators=[wtforms.validators.Optional()]) 747 748 chroot = wtforms.SelectField( 749 'Mock chroot', 750 choices=[], 751 default='fedora-latest-x86_64', 752 ) 753 754 resultdir = wtforms.StringField( 755 "Result directory", 756 validators=[wtforms.validators.Optional()]) 757
758 - def __init__(self, *args, **kwargs):
759 super(PackageFormCustom, self).__init__(*args, **kwargs) 760 chroot_objects = models.MockChroot.query.filter(models.MockChroot.is_active).all() 761 762 chroots = [c.name for c in chroot_objects] 763 chroots.sort() 764 chroots = [(name, name) for name in chroots] 765 766 arches = set() 767 for ch in chroot_objects: 768 if ch.os_release == 'fedora': 769 arches.add(ch.arch) 770 771 self.chroot.choices = [] 772 if arches: 773 self.chroot.choices += [('fedora-latest-' + l, 'fedora-latest-' + l) for l in arches] 774 775 self.chroot.choices += chroots
776 777 @property
778 - def source_json(self):
779 return json.dumps({ 780 "script": self.script.data, 781 "chroot": self.chroot.data, 782 "builddeps": self.builddeps.data, 783 "resultdir": self.resultdir.data, 784 })
785
786 787 -class RebuildAllPackagesFormFactory(object):
788 - def __new__(cls, active_chroots, package_names):
789 form_cls = BaseBuildFormFactory(active_chroots, FlaskForm) 790 form_cls.packages = MultiCheckboxField( 791 "Packages", 792 choices=[(name, name) for name in package_names], 793 default=package_names, 794 validators=[wtforms.validators.DataRequired()]) 795 return form_cls
796
797 798 -class BaseBuildFormFactory(object):
799 - def __new__(cls, active_chroots, form, package=None):
800 class F(form): 801 @property 802 def selected_chroots(self): 803 selected = [] 804 for ch in self.chroots_list: 805 if getattr(self, ch).data: 806 selected.append(ch) 807 return selected
808 809 F.memory_reqs = wtforms.IntegerField( 810 "Memory requirements", 811 validators=[ 812 wtforms.validators.Optional(), 813 wtforms.validators.NumberRange( 814 min=constants.MIN_BUILD_MEMORY, 815 max=constants.MAX_BUILD_MEMORY)], 816 default=constants.DEFAULT_BUILD_MEMORY) 817 818 F.timeout = wtforms.IntegerField( 819 "Timeout", 820 validators=[ 821 wtforms.validators.Optional(), 822 wtforms.validators.NumberRange( 823 min=constants.MIN_BUILD_TIMEOUT, 824 max=constants.MAX_BUILD_TIMEOUT)], 825 default=constants.DEFAULT_BUILD_TIMEOUT) 826 827 F.enable_net = wtforms.BooleanField(false_values=FALSE_VALUES) 828 F.background = wtforms.BooleanField(default=False, false_values=FALSE_VALUES) 829 F.project_dirname = wtforms.StringField(default=None) 830 831 # overrides BasePackageForm.package_name and is unused for building 832 F.package_name = wtforms.StringField() 833 834 # fill chroots based on project settings 835 F.chroots_list = [x.name for x in active_chroots] 836 F.chroots_list.sort() 837 F.chroots_sets = {} 838 839 package_chroots = set(F.chroots_list) 840 if package: 841 package_chroots = set([ch.name for ch in package.chroots]) 842 843 for ch in F.chroots_list: 844 default = ch in package_chroots 845 setattr(F, ch, wtforms.BooleanField(ch, default=default, false_values=FALSE_VALUES)) 846 if ch[0] in F.chroots_sets: 847 F.chroots_sets[ch[0]].append(ch) 848 else: 849 F.chroots_sets[ch[0]] = [ch] 850 return F 851
852 853 -class BuildFormScmFactory(object):
854 - def __new__(cls, active_chroots, package=None):
856
857 858 -class BuildFormTitoFactory(object):
859 """ 860 @deprecated 861 """
862 - def __new__(cls, active_chroots):
864
865 866 -class BuildFormMockFactory(object):
867 """ 868 @deprecated 869 """
870 - def __new__(cls, active_chroots):
872
873 874 -class BuildFormPyPIFactory(object):
875 - def __new__(cls, active_chroots, package=None):
877
878 879 -class BuildFormRubyGemsFactory(object):
880 - def __new__(cls, active_chroots, package=None):
882
883 884 -class BuildFormDistGitFactory(object):
885 - def __new__(cls, active_chroots):
887
888 889 -class BuildFormUploadFactory(object):
890 - def __new__(cls, active_chroots):
891 form = BaseBuildFormFactory(active_chroots, FlaskForm) 892 form.pkgs = FileField('srpm', validators=[ 893 FileRequired(), 894 SrpmValidator()]) 895 return form
896
897 898 -class BuildFormCustomFactory(object):
899 - def __new__(cls, active_chroots, package=None):
901
902 903 -class BuildFormUrlFactory(object):
904 - def __new__(cls, active_chroots):
905 form = BaseBuildFormFactory(active_chroots, FlaskForm) 906 form.pkgs = wtforms.TextAreaField( 907 "Pkgs", 908 validators=[ 909 wtforms.validators.DataRequired(message="URLs to packages are required"), 910 UrlListValidator(), 911 UrlSrpmListValidator()], 912 filters=[StringListFilter()]) 913 return form
914
915 916 -class ModuleFormUploadFactory(FlaskForm):
917 modulemd = FileField("modulemd", validators=[ 918 FileRequired(), 919 # @TODO Validate modulemd.yaml file 920 ]) 921 922 create = wtforms.BooleanField("create", default=True, false_values=FALSE_VALUES) 923 build = wtforms.BooleanField("build", default=True, false_values=FALSE_VALUES)
924
925 926 -class ModuleBuildForm(FlaskForm):
927 modulemd = FileField("modulemd") 928 scmurl = wtforms.StringField() 929 branch = wtforms.StringField()
930
931 932 -class PagureIntegrationForm(FlaskForm):
933 repo_url = wtforms.StringField("repo_url", default='') 934 api_key = wtforms.StringField("api_key", default='') 935
936 - def __init__(self, api_key=None, repo_url=None, *args, **kwargs):
937 super(PagureIntegrationForm, self).__init__(*args, **kwargs) 938 if api_key != None: 939 self.api_key.data = api_key 940 if repo_url != None: 941 self.repo_url.data = repo_url
942
943 944 -class ChrootForm(FlaskForm):
945 946 """ 947 Validator for editing chroots in project 948 (adding packages to minimal chroot) 949 """ 950 951 buildroot_pkgs = wtforms.TextField("Packages") 952 953 repos = wtforms.TextAreaField('Repos', 954 validators=[UrlRepoListValidator(), 955 wtforms.validators.Optional()], 956 filters=[StringListFilter()]) 957 958 comps = FileField("comps_xml") 959 960 with_opts = wtforms.TextField("With options") 961 without_opts = wtforms.TextField("Without options")
962
963 964 -class CoprChrootExtend(FlaskForm):
965 extend = wtforms.StringField("Chroot name") 966 expire = wtforms.StringField("Chroot name")
967
968 969 -class CoprLegalFlagForm(FlaskForm):
970 comment = wtforms.TextAreaField("Comment")
971
972 973 -class PermissionsApplierFormFactory(object):
974 975 @staticmethod
976 - def create_form_cls(permission=None):
977 class F(FlaskForm): 978 pass
979 980 builder_default = False 981 admin_default = False 982 983 if permission: 984 if permission.copr_builder != helpers.PermissionEnum("nothing"): 985 builder_default = True 986 if permission.copr_admin != helpers.PermissionEnum("nothing"): 987 admin_default = True 988 989 setattr(F, "copr_builder", 990 wtforms.BooleanField( 991 default=builder_default, 992 false_values=FALSE_VALUES, 993 filters=[ValueToPermissionNumberFilter()])) 994 995 setattr(F, "copr_admin", 996 wtforms.BooleanField( 997 default=admin_default, 998 false_values=FALSE_VALUES, 999 filters=[ValueToPermissionNumberFilter()])) 1000 1001 return F
1002
1003 1004 -class PermissionsFormFactory(object):
1005 1006 """Creates a dynamic form for given set of copr permissions""" 1007 @staticmethod
1008 - def create_form_cls(permissions):
1009 class F(FlaskForm): 1010 pass
1011 1012 for perm in permissions: 1013 builder_choices = helpers.PermissionEnum.choices_list() 1014 admin_choices = helpers.PermissionEnum.choices_list() 1015 1016 builder_default = perm.copr_builder 1017 admin_default = perm.copr_admin 1018 1019 setattr(F, "copr_builder_{0}".format(perm.user.id), 1020 wtforms.SelectField( 1021 choices=builder_choices, 1022 default=builder_default, 1023 coerce=int)) 1024 1025 setattr(F, "copr_admin_{0}".format(perm.user.id), 1026 wtforms.SelectField( 1027 choices=admin_choices, 1028 default=admin_default, 1029 coerce=int)) 1030 1031 return F
1032
1033 1034 -class CoprModifyForm(FlaskForm):
1035 description = wtforms.TextAreaField('Description', 1036 validators=[wtforms.validators.Optional()]) 1037 1038 instructions = wtforms.TextAreaField('Instructions', 1039 validators=[wtforms.validators.Optional()]) 1040 1041 chroots = wtforms.TextAreaField('Chroots', 1042 validators=[wtforms.validators.Optional(), ChrootsValidator()]) 1043 1044 repos = wtforms.TextAreaField('Repos', 1045 validators=[UrlRepoListValidator(), 1046 wtforms.validators.Optional()], 1047 filters=[StringListFilter()]) 1048 1049 disable_createrepo = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1050 unlisted_on_hp = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1051 auto_prune = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1052 use_bootstrap_container = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1053 follow_fedora_branching = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1054 follow_fedora_branching = wtforms.BooleanField(default=True, false_values=FALSE_VALUES) 1055 delete_after_days = wtforms.IntegerField( 1056 validators=[wtforms.validators.Optional(), 1057 wtforms.validators.NumberRange(min=-1, max=60)], 1058 filters=[(lambda x : -1 if x is None else x)]) 1059 1060 # Deprecated, use `enable_net` instead 1061 build_enable_net = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES) 1062 enable_net = wtforms.BooleanField(validators=[wtforms.validators.Optional()], false_values=FALSE_VALUES)
1063
1064 1065 -class CoprForkFormFactory(object):
1066 @staticmethod
1067 - def create_form_cls(copr, user, groups):
1068 class F(FlaskForm): 1069 source = wtforms.StringField( 1070 "Source", 1071 default=copr.full_name) 1072 1073 owner = wtforms.SelectField( 1074 "Fork owner", 1075 choices=[(user.name, user.name)] + [(g.at_name, g.at_name) for g in groups], 1076 default=user.name, 1077 validators=[wtforms.validators.DataRequired()]) 1078 1079 name = wtforms.StringField( 1080 "Fork name", 1081 default=copr.name, 1082 validators=[wtforms.validators.DataRequired(), NameCharactersValidator()]) 1083 1084 confirm = wtforms.BooleanField( 1085 "Confirm", 1086 false_values=FALSE_VALUES, 1087 default=False)
1088 return F
1089
1090 1091 -class ModifyChrootForm(ChrootForm):
1092 buildroot_pkgs = wtforms.TextField('Additional packages to be always present in minimal buildroot') 1093 repos = wtforms.TextAreaField('Additional repos to be used for builds in chroot', 1094 validators=[UrlRepoListValidator(), 1095 wtforms.validators.Optional()], 1096 filters=[StringListFilter()]) 1097 comps = None 1098 upload_comps = FileField("Upload comps.xml") 1099 delete_comps = wtforms.BooleanField("Delete comps.xml", false_values=FALSE_VALUES)
1100
1101 -class AdminPlaygroundForm(FlaskForm):
1102 playground = wtforms.BooleanField("Playground", false_values=FALSE_VALUES)
1103
1104 1105 -class AdminPlaygroundSearchForm(FlaskForm):
1106 project = wtforms.TextField("Project")
1107
1108 1109 -class GroupUniqueNameValidator(object):
1110
1111 - def __init__(self, message=None):
1112 if not message: 1113 message = "Group with the alias '{}' already exists." 1114 self.message = message
1115
1116 - def __call__(self, form, field):
1117 if UsersLogic.group_alias_exists(field.data): 1118 raise wtforms.ValidationError(self.message.format(field.data))
1119
1120 1121 -class ActivateFasGroupForm(FlaskForm):
1122 1123 name = wtforms.StringField( 1124 validators=[ 1125 wtforms.validators.Regexp( 1126 re.compile(r"^[\w.-]+$"), 1127 message="Name must contain only letters," 1128 "digits, underscores, dashes and dots."), 1129 GroupUniqueNameValidator() 1130 ] 1131 )
1132
1133 1134 -class CreateModuleForm(FlaskForm):
1135 builds = wtforms.FieldList(wtforms.StringField("Builds ID list")) 1136 packages = wtforms.FieldList(wtforms.StringField("Packages list")) 1137 filter = wtforms.FieldList(wtforms.StringField("Package Filter")) 1138 api = wtforms.FieldList(wtforms.StringField("Module API")) 1139 profile_names = wtforms.FieldList(wtforms.StringField("Install Profiles"), min_entries=2) 1140 profile_pkgs = wtforms.FieldList(wtforms.FieldList(wtforms.StringField("Install Profiles")), min_entries=2) 1141
1142 - def __init__(self, copr=None, *args, **kwargs):
1143 self.copr = copr 1144 super(CreateModuleForm, self).__init__(*args, **kwargs)
1145
1146 - def validate(self):
1147 if not FlaskForm.validate(self): 1148 return False 1149 1150 # Profile names should be unique 1151 names = [x for x in self.profile_names.data if x] 1152 if len(set(names)) < len(names): 1153 self.errors["profiles"] = ["Profile names must be unique"] 1154 return False 1155 1156 # WORKAROUND 1157 # profile_pkgs are somehow sorted so if I fill profile_name in the first box and 1158 # profile_pkgs in seconds box, it is sorted and validated correctly 1159 for i in range(0, len(self.profile_names.data)): 1160 # If profile name is not set, then there should not be any packages in this profile 1161 if not flask.request.form["profile_names-{}".format(i)]: 1162 if [j for j in range(0, len(self.profile_names)) if "profile_pkgs-{}-{}".format(i, j) in flask.request.form]: 1163 self.errors["profiles"] = ["Missing profile name"] 1164 return False 1165 return True
1166
1167 1168 -class ModuleRepo(FlaskForm):
1169 owner = wtforms.StringField("Owner Name", validators=[wtforms.validators.DataRequired()]) 1170 copr = wtforms.StringField("Copr Name", validators=[wtforms.validators.DataRequired()]) 1171 name = wtforms.StringField("Name", validators=[wtforms.validators.DataRequired()]) 1172 stream = wtforms.StringField("Stream", validators=[wtforms.validators.DataRequired()]) 1173 version = wtforms.IntegerField("Version", validators=[wtforms.validators.DataRequired()]) 1174 arch = wtforms.StringField("Arch", validators=[wtforms.validators.DataRequired()])
1175