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 FileAllowed, FileRequired, FileField 
  9   
 10  from flask_wtf import Form as FlaskForm 
 11  from jinja2 import Markup 
 12   
 13  from coprs import constants 
 14  from coprs import helpers 
 15  from coprs import models 
 16  from coprs.logic.coprs_logic import CoprsLogic 
 17  from coprs.logic.users_logic import UsersLogic 
 18  from coprs.logic.modules_logic import ModulesLogic 
 19  from coprs.models import Package 
 20  from coprs import exceptions 
21 22 -def get_package_form_cls_by_source_type_text(source_type_text):
23 """ 24 Params 25 ------ 26 source_type_text : str 27 name of the source type (tito/mock/pypi/rubygems/upload) 28 29 Returns 30 ------- 31 BasePackageForm child 32 based on source_type_text input 33 """ 34 if source_type_text == 'git_and_tito': 35 return PackageFormTito 36 elif source_type_text == 'mock_scm': 37 return PackageFormMock 38 elif source_type_text == 'pypi': 39 return PackageFormPyPI 40 elif source_type_text == 'rubygems': 41 return PackageFormRubyGems 42 else: 43 raise exceptions.UnknownSourceTypeException("Invalid source type")
44
45 46 -class MultiCheckboxField(wtforms.SelectMultipleField):
47 widget = wtforms.widgets.ListWidget(prefix_label=False) 48 option_widget = wtforms.widgets.CheckboxInput()
49
50 51 -class UrlListValidator(object):
52
53 - def __init__(self, message=None):
54 if not message: 55 message = ("A list of http[s] URLs separated by whitespace characters" 56 " is needed ('{0}' doesn't seem to be a valid URL).") 57 self.message = message
58
59 - def __call__(self, form, field):
60 urls = field.data.split() 61 for u in urls: 62 if not self.is_url(u): 63 raise wtforms.ValidationError(self.message.format(u))
64
65 - def is_url(self, url):
66 parsed = urlparse(url) 67 if not parsed.scheme.startswith("http"): 68 return False 69 if not parsed.netloc: 70 return False 71 return True
72
73 74 -class UrlRepoListValidator(UrlListValidator):
75 """ Allows also `repo://` schema"""
76 - def is_url(self, url):
77 parsed = urlparse(url) 78 if parsed.scheme not in ["http", "https", "copr"]: 79 return False 80 if not parsed.netloc: 81 return False 82 # copr://username/projectname 83 # ^^ schema ^^ netlock ^^ path 84 if parsed.scheme == "copr": 85 # check if projectname missed 86 path_split = parsed.path.split("/") 87 if len(path_split) < 2 or path_split[1] == "": 88 return False 89 90 return True
91
92 93 -class UrlSrpmListValidator(UrlListValidator):
94 - def __init__(self, message=None):
95 if not message: 96 message = ("URLs must end with .src.rpm, .nosrc.rpm, or .spec" 97 " ('{0}' doesn't seem to be a valid URL).") 98 super(UrlSrpmListValidator, self).__init__(message)
99
100 - def is_url(self, url):
101 parsed = urlparse(url) 102 if not parsed.path.endswith((".src.rpm", ".nosrc.rpm", ".spec")): 103 return False 104 return True
105
106 107 -class SrpmValidator(object):
108 - def __init__(self, message=None):
109 if not message: 110 message = "You can upload only .src.rpm, .nosrc.rpm, and .spec files" 111 self.message = message
112
113 - def __call__(self, form, field):
114 filename = field.data.filename.lower() 115 if not filename.endswith((".src.rpm", ".nosrc.rpm", ".spec")): 116 raise wtforms.ValidationError(self.message)
117
118 119 -class CoprUniqueNameValidator(object):
120
121 - def __init__(self, message=None, user=None, group=None):
122 if not message: 123 if group is None: 124 message = "You already have project named '{}'." 125 else: 126 message = "Group {} ".format(group) + "already have project named '{}'." 127 self.message = message 128 if not user: 129 user = flask.g.user 130 self.user = user 131 self.group = group
132
133 - def __call__(self, form, field):
134 if self.group: 135 existing = CoprsLogic.exists_for_group( 136 self.group, field.data).first() 137 else: 138 existing = CoprsLogic.exists_for_user( 139 self.user, field.data).first() 140 141 if existing and str(existing.id) != form.id.data: 142 raise wtforms.ValidationError(self.message.format(field.data))
143
144 145 -class NameCharactersValidator(object):
146 - def __init__(self, message=None):
147 if not message: 148 message = "Name must contain only letters, digits, underscores, dashes and dots." 149 self.message = message
150
151 - def __call__(self, form, field):
152 validator = wtforms.validators.Regexp( 153 re.compile(r"^[\w.-]+$"), 154 message=self.message) 155 validator(form, field)
156
157 158 -class NameNotNumberValidator(object):
159
160 - def __init__(self, message=None):
161 if not message: 162 message = "Project's name can not be just number." 163 self.message = message
164
165 - def __call__(self, form, field):
166 if field.data.isdigit(): 167 raise wtforms.ValidationError(self.message.format(field.data))
168
169 170 -class EmailOrURL(object):
171
172 - def __init__(self, message=None):
173 if not message: 174 message = "{} must be email address or URL" 175 self.message = message
176
177 - def __call__(self, form, field):
178 for validator in [wtforms.validators.Email(), wtforms.validators.URL()]: 179 try: 180 validator(form, field) 181 return True 182 except wtforms.ValidationError: 183 pass 184 raise wtforms.ValidationError(self.message.format(field.name.capitalize()))
185
186 187 -class StringListFilter(object):
188
189 - def __call__(self, value):
190 if not value: 191 return '' 192 # Replace every whitespace string with one newline 193 # Formats ideally for html form filling, use replace('\n', ' ') 194 # to get space-separated values or split() to get list 195 result = value.strip() 196 regex = re.compile(r"\s+") 197 return regex.sub(lambda x: '\n', result)
198
199 200 -class ValueToPermissionNumberFilter(object):
201
202 - def __call__(self, value):
203 if value: 204 return helpers.PermissionEnum("request") 205 return helpers.PermissionEnum("nothing")
206
207 208 -class CoprFormFactory(object):
209 210 @staticmethod
211 - def create_form_cls(mock_chroots=None, user=None, group=None):
212 class F(FlaskForm): 213 # also use id here, to be able to find out whether user 214 # is updating a copr if so, we don't want to shout 215 # that name already exists 216 id = wtforms.HiddenField() 217 group_id = wtforms.HiddenField() 218 219 name = wtforms.StringField( 220 "Name", 221 validators=[ 222 wtforms.validators.DataRequired(), 223 NameCharactersValidator(), 224 CoprUniqueNameValidator(user=user, group=group), 225 NameNotNumberValidator() 226 ]) 227 228 homepage = wtforms.StringField( 229 "Homepage", 230 validators=[ 231 wtforms.validators.Optional(), 232 wtforms.validators.URL()]) 233 234 contact = wtforms.StringField( 235 "Contact", 236 validators=[ 237 wtforms.validators.Optional(), 238 EmailOrURL()]) 239 240 description = wtforms.TextAreaField("Description") 241 242 instructions = wtforms.TextAreaField("Instructions") 243 244 repos = wtforms.TextAreaField( 245 "External Repositories", 246 validators=[UrlRepoListValidator()], 247 filters=[StringListFilter()]) 248 249 initial_pkgs = wtforms.TextAreaField( 250 "Initial packages to build", 251 validators=[ 252 UrlListValidator(), 253 UrlSrpmListValidator()], 254 filters=[StringListFilter()]) 255 256 disable_createrepo = wtforms.BooleanField(default=False) 257 build_enable_net = wtforms.BooleanField(default=False) 258 unlisted_on_hp = wtforms.BooleanField("Do not display this project on home page", default=False) 259 persistent = wtforms.BooleanField(default=False) 260 auto_prune = wtforms.BooleanField("If backend auto-prunning script should be run for this project", default=True) 261 use_bootstrap_container = wtforms.BooleanField("Enable use_bootstrap_container mock's feature (experimental)", default=False) 262 263 @property 264 def selected_chroots(self): 265 selected = [] 266 for ch in self.chroots_list: 267 if getattr(self, ch).data: 268 selected.append(ch) 269 return selected
270 271 def validate(self): 272 if not super(F, self).validate(): 273 return False 274 275 if not self.validate_mock_chroots_not_empty(): 276 self.errors["chroots"] = ["At least one chroot must be selected"] 277 return False 278 return True
279 280 def validate_mock_chroots_not_empty(self): 281 have_any = False 282 for c in self.chroots_list: 283 if getattr(self, c).data: 284 have_any = True 285 return have_any 286 287 F.chroots_list = list(map(lambda x: x.name, 288 models.MockChroot.query.filter( 289 models.MockChroot.is_active == True 290 ).all())) 291 F.chroots_list.sort() 292 # sets of chroots according to how we should print them in columns 293 F.chroots_sets = {} 294 for ch in F.chroots_list: 295 checkbox_default = False 296 if mock_chroots and ch in map(lambda x: x.name, 297 mock_chroots): 298 checkbox_default = True 299 300 setattr(F, ch, wtforms.BooleanField(ch, default=checkbox_default)) 301 if ch[0] in F.chroots_sets: 302 F.chroots_sets[ch[0]].append(ch) 303 else: 304 F.chroots_sets[ch[0]] = [ch] 305 306 return F 307
308 309 -class CoprDeleteForm(FlaskForm):
310 verify = wtforms.TextField( 311 "Confirm deleting by typing 'yes'", 312 validators=[ 313 wtforms.validators.Required(), 314 wtforms.validators.Regexp( 315 r"^yes$", 316 message="Type 'yes' - without the quotes, lowercase.") 317 ])
318
319 320 # @TODO jkadlcik - rewrite via BaseBuildFormFactory after fe-dev-cloud is back online 321 -class BuildFormRebuildFactory(object):
322 @staticmethod
323 - def create_form_cls(active_chroots):
324 class F(FlaskForm): 325 @property 326 def selected_chroots(self): 327 selected = [] 328 for ch in self.chroots_list: 329 if getattr(self, ch).data: 330 selected.append(ch) 331 return selected
332 333 memory_reqs = wtforms.IntegerField( 334 "Memory requirements", 335 validators=[ 336 wtforms.validators.NumberRange( 337 min=constants.MIN_BUILD_MEMORY, 338 max=constants.MAX_BUILD_MEMORY)], 339 default=constants.DEFAULT_BUILD_MEMORY) 340 341 timeout = wtforms.IntegerField( 342 "Timeout", 343 validators=[ 344 wtforms.validators.NumberRange( 345 min=constants.MIN_BUILD_TIMEOUT, 346 max=constants.MAX_BUILD_TIMEOUT)], 347 default=constants.DEFAULT_BUILD_TIMEOUT) 348 349 enable_net = wtforms.BooleanField()
350 351 F.chroots_list = list(map(lambda x: x.name, active_chroots)) 352 F.chroots_list.sort() 353 F.chroots_sets = {} 354 for ch in F.chroots_list: 355 setattr(F, ch, wtforms.BooleanField(ch, default=True)) 356 if ch[0] in F.chroots_sets: 357 F.chroots_sets[ch[0]].append(ch) 358 else: 359 F.chroots_sets[ch[0]] = [ch] 360 361 return F 362
363 364 -class BasePackageForm(FlaskForm):
365 package_name = wtforms.StringField( 366 "Package name", 367 validators=[wtforms.validators.DataRequired()]) 368 webhook_rebuild = wtforms.BooleanField(default=False)
369
370 371 -class PackageFormTito(BasePackageForm):
372 git_url = wtforms.StringField( 373 "Git URL", 374 validators=[ 375 wtforms.validators.DataRequired(), 376 wtforms.validators.URL()]) 377 378 git_directory = wtforms.StringField( 379 "Git Directory", 380 validators=[ 381 wtforms.validators.Optional()]) 382 383 git_branch = wtforms.StringField( 384 "Git Branch", 385 validators=[ 386 wtforms.validators.Optional()]) 387 388 tito_test = wtforms.BooleanField(default=False) 389 390 @property
391 - def source_json(self):
392 return json.dumps({ 393 "git_url": self.git_url.data, 394 "git_branch": self.git_branch.data, 395 "git_dir": self.git_directory.data, 396 "tito_test": self.tito_test.data 397 })
398
399 400 -class PackageFormMock(BasePackageForm):
401 scm_type = wtforms.SelectField( 402 "SCM Type", 403 choices=[("git", "Git"), ("svn", "SVN")]) 404 405 scm_url = wtforms.StringField( 406 "SCM URL", 407 validators=[ 408 wtforms.validators.DataRequired(), 409 wtforms.validators.URL()]) 410 411 scm_branch = wtforms.StringField( 412 "Git Branch", 413 validators=[ 414 wtforms.validators.Optional()]) 415 416 scm_subdir = wtforms.StringField( 417 "Subdirectory", 418 validators=[ 419 wtforms.validators.Optional()]) 420 421 spec = wtforms.StringField( 422 "Spec File", 423 validators=[ 424 wtforms.validators.Optional(), 425 wtforms.validators.Regexp( 426 "^.+\.spec$", 427 message="RPM spec file must end with .spec")]) 428 429 @property
430 - def source_json(self):
431 return json.dumps({ 432 "scm_type": self.scm_type.data, 433 "scm_url": self.scm_url.data, 434 "scm_subdir": self.scm_subdir.data, 435 "scm_branch": self.scm_branch.data, 436 "spec": self.spec.data 437 })
438
439 440 -class PackageFormPyPI(BasePackageForm):
441 pypi_package_name = wtforms.StringField( 442 "PyPI package name", 443 validators=[wtforms.validators.DataRequired()]) 444 445 pypi_package_version = wtforms.StringField( 446 "PyPI package version", 447 validators=[ 448 wtforms.validators.Optional(), 449 ]) 450 451 python_versions = MultiCheckboxField( 452 'Build for Python', 453 choices=[ 454 ('3', 'python3'), 455 ('2', 'python2') 456 ], 457 default=['3', '2']) 458 459 @property
460 - def source_json(self):
461 return json.dumps({ 462 "pypi_package_name": self.pypi_package_name.data, 463 "pypi_package_version": self.pypi_package_version.data, 464 "python_versions": self.python_versions.data 465 })
466
467 468 -class PackageFormRubyGems(BasePackageForm):
469 gem_name = wtforms.StringField( 470 "Gem Name", 471 validators=[wtforms.validators.DataRequired()]) 472 473 @property
474 - def source_json(self):
475 return json.dumps({ 476 "gem_name": self.gem_name.data 477 })
478
479 480 -class PackageFormDistGit(BasePackageForm):
481 clone_url = wtforms.StringField( 482 "Clone Url", 483 validators=[wtforms.validators.DataRequired()]) 484 485 branch = wtforms.StringField( 486 "Branch", 487 validators=[wtforms.validators.Optional()]) 488 489 @property
490 - def source_json(self):
491 return json.dumps({ 492 "clone_url": self.clone_url.data, 493 "branch": self.branch.data 494 })
495
496 497 -class RebuildAllPackagesFormFactory(object):
498 - def __new__(cls, active_chroots, package_names):
499 form_cls = BaseBuildFormFactory(active_chroots, FlaskForm) 500 form_cls.packages = MultiCheckboxField( 501 "Packages", 502 choices=[(name, name) for name in package_names], 503 default=package_names, 504 validators=[wtforms.validators.DataRequired()]) 505 return form_cls
506
507 508 -class BaseBuildFormFactory(object):
509 - def __new__(cls, active_chroots, form):
510 class F(form): 511 @property 512 def selected_chroots(self): 513 selected = [] 514 for ch in self.chroots_list: 515 if getattr(self, ch).data: 516 selected.append(ch) 517 return selected
518 519 F.memory_reqs = wtforms.IntegerField( 520 "Memory requirements", 521 validators=[ 522 wtforms.validators.Optional(), 523 wtforms.validators.NumberRange( 524 min=constants.MIN_BUILD_MEMORY, 525 max=constants.MAX_BUILD_MEMORY)], 526 default=constants.DEFAULT_BUILD_MEMORY) 527 528 F.timeout = wtforms.IntegerField( 529 "Timeout", 530 validators=[ 531 wtforms.validators.Optional(), 532 wtforms.validators.NumberRange( 533 min=constants.MIN_BUILD_TIMEOUT, 534 max=constants.MAX_BUILD_TIMEOUT)], 535 default=constants.DEFAULT_BUILD_TIMEOUT) 536 537 F.enable_net = wtforms.BooleanField() 538 F.background = wtforms.BooleanField(default=False) 539 540 # overrides BasePackageForm.package_name and is unused for building 541 F.package_name = wtforms.StringField() 542 543 F.chroots_list = list(map(lambda x: x.name, active_chroots)) 544 F.chroots_list.sort() 545 F.chroots_sets = {} 546 for ch in F.chroots_list: 547 setattr(F, ch, wtforms.BooleanField(ch, default=True)) 548 if ch[0] in F.chroots_sets: 549 F.chroots_sets[ch[0]].append(ch) 550 else: 551 F.chroots_sets[ch[0]] = [ch] 552 return F 553
554 555 -class BuildFormTitoFactory(object):
556 - def __new__(cls, active_chroots):
558
559 560 -class BuildFormMockFactory(object):
561 - def __new__(cls, active_chroots):
563
564 565 -class BuildFormPyPIFactory(object):
566 - def __new__(cls, active_chroots):
568
569 570 -class BuildFormRubyGemsFactory(object):
571 - def __new__(cls, active_chroots):
573
574 575 -class BuildFormDistGitFactory(object):
576 - def __new__(cls, active_chroots):
578
579 580 -class BuildFormUploadFactory(object):
581 - def __new__(cls, active_chroots):
582 form = BaseBuildFormFactory(active_chroots, FlaskForm) 583 form.pkgs = FileField('srpm', validators=[ 584 FileRequired(), 585 SrpmValidator()]) 586 return form
587
588 589 -class BuildFormUrlFactory(object):
590 - def __new__(cls, active_chroots):
591 form = BaseBuildFormFactory(active_chroots, FlaskForm) 592 form.pkgs = wtforms.TextAreaField( 593 "Pkgs", 594 validators=[ 595 wtforms.validators.DataRequired(message="URLs to packages are required"), 596 UrlListValidator(), 597 UrlSrpmListValidator()], 598 filters=[StringListFilter()]) 599 return form
600
601 602 -class ModuleFormUploadFactory(FlaskForm):
603 modulemd = FileField("modulemd", validators=[ 604 FileRequired(), 605 # @TODO Validate modulemd.yaml file 606 ]) 607 608 create = wtforms.BooleanField("create", default=True) 609 build = wtforms.BooleanField("build", default=True)
610
611 612 -class ModuleBuildForm(FlaskForm):
613 modulemd = FileField("modulemd") 614 scmurl = wtforms.StringField() 615 branch = wtforms.StringField() 616 copr_owner = wtforms.StringField() 617 copr_project = wtforms.StringField()
618
619 620 -class ChrootForm(FlaskForm):
621 622 """ 623 Validator for editing chroots in project 624 (adding packages to minimal chroot) 625 """ 626 627 buildroot_pkgs = wtforms.TextField( 628 "Packages") 629 630 repos = wtforms.TextAreaField('Repos', 631 validators=[UrlRepoListValidator(), 632 wtforms.validators.Optional()], 633 filters=[StringListFilter()]) 634 635 module_md = FileField("module_md") 636 637 comps = FileField("comps_xml")
638
639 640 -class CoprLegalFlagForm(FlaskForm):
641 comment = wtforms.TextAreaField("Comment")
642
643 644 -class PermissionsApplierFormFactory(object):
645 646 @staticmethod
647 - def create_form_cls(permission=None):
648 class F(FlaskForm): 649 pass
650 651 builder_default = False 652 admin_default = False 653 654 if permission: 655 if permission.copr_builder != helpers.PermissionEnum("nothing"): 656 builder_default = True 657 if permission.copr_admin != helpers.PermissionEnum("nothing"): 658 admin_default = True 659 660 setattr(F, "copr_builder", 661 wtforms.BooleanField( 662 default=builder_default, 663 filters=[ValueToPermissionNumberFilter()])) 664 665 setattr(F, "copr_admin", 666 wtforms.BooleanField( 667 default=admin_default, 668 filters=[ValueToPermissionNumberFilter()])) 669 670 return F
671
672 673 -class PermissionsFormFactory(object):
674 675 """Creates a dynamic form for given set of copr permissions""" 676 @staticmethod
677 - def create_form_cls(permissions):
678 class F(FlaskForm): 679 pass
680 681 for perm in permissions: 682 builder_choices = helpers.PermissionEnum.choices_list() 683 admin_choices = helpers.PermissionEnum.choices_list() 684 685 builder_default = perm.copr_builder 686 admin_default = perm.copr_admin 687 688 setattr(F, "copr_builder_{0}".format(perm.user.id), 689 wtforms.SelectField( 690 choices=builder_choices, 691 default=builder_default, 692 coerce=int)) 693 694 setattr(F, "copr_admin_{0}".format(perm.user.id), 695 wtforms.SelectField( 696 choices=admin_choices, 697 default=admin_default, 698 coerce=int)) 699 700 return F
701
702 703 -class CoprModifyForm(FlaskForm):
704 description = wtforms.TextAreaField('Description', 705 validators=[wtforms.validators.Optional()]) 706 707 instructions = wtforms.TextAreaField('Instructions', 708 validators=[wtforms.validators.Optional()]) 709 710 repos = wtforms.TextAreaField('Repos', 711 validators=[UrlRepoListValidator(), 712 wtforms.validators.Optional()], 713 filters=[StringListFilter()]) 714 715 disable_createrepo = wtforms.BooleanField(validators=[wtforms.validators.Optional()]) 716 unlisted_on_hp = wtforms.BooleanField(validators=[wtforms.validators.Optional()]) 717 build_enable_net = wtforms.BooleanField(validators=[wtforms.validators.Optional()]) 718 auto_prune = wtforms.BooleanField(validators=[wtforms.validators.Optional()]) 719 use_bootstrap_container = wtforms.BooleanField(validators=[wtforms.validators.Optional()])
720
721 722 -class CoprForkFormFactory(object):
723 @staticmethod
724 - def create_form_cls(copr, user, groups):
725 class F(FlaskForm): 726 source = wtforms.StringField( 727 "Source", 728 default=copr.full_name) 729 730 owner = wtforms.SelectField( 731 "Fork owner", 732 choices=[(user.name, user.name)] + [(g.at_name, g.at_name) for g in groups], 733 default=user.name, 734 validators=[wtforms.validators.DataRequired()]) 735 736 name = wtforms.StringField( 737 "Fork name", 738 default=copr.name, 739 validators=[wtforms.validators.DataRequired(), NameCharactersValidator()]) 740 741 confirm = wtforms.BooleanField( 742 "Confirm", 743 default=False)
744 return F
745
746 747 -class ModifyChrootForm(FlaskForm):
748 buildroot_pkgs = wtforms.TextField('Additional packages to be always present in minimal buildroot') 749 repos = wtforms.TextAreaField('Additional repos to be used for builds in chroot', 750 validators=[UrlRepoListValidator(), 751 wtforms.validators.Optional()], 752 filters=[StringListFilter()]) 753 upload_comps = FileField("Upload comps.xml") 754 delete_comps = wtforms.BooleanField("Delete comps.xml")
755
756 757 -class AdminPlaygroundForm(FlaskForm):
758 playground = wtforms.BooleanField("Playground")
759
760 761 -class AdminPlaygroundSearchForm(FlaskForm):
762 project = wtforms.TextField("Project")
763
764 765 -class GroupUniqueNameValidator(object):
766
767 - def __init__(self, message=None):
768 if not message: 769 message = "Group with the alias '{}' already exists." 770 self.message = message
771
772 - def __call__(self, form, field):
773 if UsersLogic.group_alias_exists(field.data): 774 raise wtforms.ValidationError(self.message.format(field.data))
775
776 777 -class ActivateFasGroupForm(FlaskForm):
778 779 name = wtforms.StringField( 780 validators=[ 781 wtforms.validators.Regexp( 782 re.compile(r"^[\w.-]+$"), 783 message="Name must contain only letters," 784 "digits, underscores, dashes and dots."), 785 GroupUniqueNameValidator() 786 ] 787 )
788
789 790 -class CreateModuleForm(FlaskForm):
791 name = wtforms.StringField("Name") 792 stream = wtforms.StringField("Stream") 793 version = wtforms.IntegerField("Version") 794 builds = wtforms.FieldList(wtforms.StringField("Builds ID list")) 795 packages = wtforms.FieldList(wtforms.StringField("Packages list")) 796 filter = wtforms.FieldList(wtforms.StringField("Package Filter")) 797 api = wtforms.FieldList(wtforms.StringField("Module API")) 798 profile_names = wtforms.FieldList(wtforms.StringField("Install Profiles"), min_entries=2) 799 profile_pkgs = wtforms.FieldList(wtforms.FieldList(wtforms.StringField("Install Profiles")), min_entries=2) 800
801 - def __init__(self, copr=None, *args, **kwargs):
802 self.copr = copr 803 super(CreateModuleForm, self).__init__(*args, **kwargs)
804
805 - def validate(self):
806 if not FlaskForm.validate(self): 807 return False 808 809 module = ModulesLogic.get_by_nsv(self.copr, self.name.data, self.stream.data, self.version.data).first() 810 if module: 811 self.errors["nsv"] = [Markup("Module <a href='{}'>{}</a> already exists".format( 812 helpers.copr_url("coprs_ns.copr_module", module.copr, id=module.id), module.full_name))] 813 return False 814 815 # Profile names should be unique 816 names = filter(None, self.profile_names.data) 817 if len(set(names)) < len(names): 818 self.errors["profiles"] = ["Profile names must be unique"] 819 return False 820 821 # WORKAROUND 822 # profile_pkgs are somehow sorted so if I fill profile_name in the first box and 823 # profile_pkgs in seconds box, it is sorted and validated correctly 824 for i in range(0, len(self.profile_names.data)): 825 # If profile name is not set, then there should not be any packages in this profile 826 if not flask.request.form["profile_names-{}".format(i)]: 827 if [j for j in range(0, len(self.profile_names)) if "profile_pkgs-{}-{}".format(i, j) in flask.request.form]: 828 self.errors["profiles"] = ["Missing profile name"] 829 return False 830 return True
831
832 833 -class ModuleRepo(FlaskForm):
834 owner = wtforms.StringField("Owner Name", validators=[wtforms.validators.DataRequired()]) 835 copr = wtforms.StringField("Copr Name", validators=[wtforms.validators.DataRequired()]) 836 name = wtforms.StringField("Name", validators=[wtforms.validators.DataRequired()]) 837 stream = wtforms.StringField("Stream", validators=[wtforms.validators.DataRequired()]) 838 version = wtforms.IntegerField("Version", validators=[wtforms.validators.DataRequired()]) 839 arch = wtforms.StringField("Arch", validators=[wtforms.validators.DataRequired()])
840