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