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