| 447 | |
| 448 | |
| 449 | class ProjectGeneralForm(StarletteForm): |
| 450 | name = StringField( |
| 451 | _l("Project name"), |
| 452 | validators=[ |
| 453 | DataRequired(), |
| 454 | Length(min=1, max=100), |
| 455 | Regexp( |
| 456 | r"^[A-Za-z0-9][A-Za-z0-9._-]*[A-Za-z0-9]$", |
| 457 | message=_l( |
| 458 | "Project names can only contain letters, numbers, hyphens, underscores and dots. They cannot start or end with a dot, underscore or hyphen." |
| 459 | ), |
| 460 | ), |
| 461 | ], |
| 462 | ) |
| 463 | avatar = FileField(_l("Avatar")) |
| 464 | delete_avatar = BooleanField(_l("Delete avatar"), default=False) |
| 465 | repo_id = IntegerField(_l("Repo ID"), validators=[DataRequired()]) |
| 466 | |
| 467 | def validate_avatar(self, field): |
| 468 | if field.data: |
| 469 | if field.data.content_type not in [ |
| 470 | "image/jpeg", |
| 471 | "image/png", |
| 472 | "image/gif", |
| 473 | "image/webp", |
| 474 | ]: |
| 475 | raise ValidationError( |
| 476 | _( |
| 477 | "Invalid file type. Only JPEG, PNG, GIF and WebP images are allowed." |
| 478 | ) |
| 479 | ) |
| 480 | if field.data.size > 10 * 1024 * 1024: # 10MB |
| 481 | raise ValidationError( |
| 482 | _("File size exceeds the maximum allowed (10MB).") |
| 483 | ) |
| 484 | |
| 485 | def __init__(self, *args, db: AsyncSession, team: Team, project: Project, **kwargs): |
| 486 | super().__init__(*args, **kwargs) |
| 487 | self.db = db |
| 488 | self.team = team |
| 489 | self.project = project |
| 490 | |
| 491 | async def async_validate_name(self, field): |
| 492 | if self.db and self.team and self.project: |
| 493 | if self.project.name != field.data: |
| 494 | result = await self.db.execute( |
| 495 | select(Project).where( |
| 496 | func.lower(Project.name) == field.data.lower(), |
| 497 | Project.team_id == self.team.id, |
| 498 | Project.id != self.project.id, |
| 499 | ) |
| 500 | ) |
| 501 | if result.scalar_one_or_none(): |
| 502 | raise ValidationError( |
| 503 | _( |
| 504 | "A project with this name already exists in this team or is reserved." |
| 505 | ) |
| 506 | ) |
nothing calls this directly
no outgoing calls
no test coverage detected