def getInputValue(self): self._error = None action = self.action_widget.getInputValue() try: announcement_date = self.announcement_date_widget.getInputValue() except ConversionError: self._error = WidgetInputError( self.name, self.label, LaunchpadValidationError( _('Please provide a valid date and time.'))) raise self._error if action == 'immediately' and announcement_date is not None: self._error = WidgetInputError( self.name, self.label, LaunchpadValidationError( _('Please do not provide a date if you want to publish ' 'immediately.'))) raise self._error if action == 'specific' and announcement_date is None: self._error = WidgetInputError( self.name, self.label, LaunchpadValidationError( _('Please provide a publication date.'))) raise self._error if action == 'immediately': return datetime.now(pytz.utc) elif action == "sometime": return None elif action == "specific": return announcement_date else: raise AssertionError('Unknown action in AnnouncementDateWidget')
def getInputValue(self): """See zope.formlib.interfaces.IInputWidget.""" scope = self.request.form_ng.getOne(self.name) if scope == 'all': return None elif scope == 'project': if not self.request.form_ng.getOne(self.target_widget.name): self._error = WidgetInputError( self.name, self.label, LaunchpadValidationError('Please enter a project name')) raise self._error try: return self.target_widget.getInputValue() except ConversionError: entered_name = self.request.form_ng.getOne("%s.target" % self.name) self._error = WidgetInputError( self.name, self.label, LaunchpadValidationError( "There is no project named '%s' registered in" " Launchpad" % entered_name)) raise self._error elif self.required: raise UnexpectedFormData("No valid option was selected.") else: return None
def _validate(self, value): """Ensure the value is a valid URI.""" uri = URI(self.normalize(value)) if self.allowed_schemes and uri.scheme not in self.allowed_schemes: raise LaunchpadValidationError( 'The URI scheme "%s" is not allowed. Only URIs with ' 'the following schemes may be used: %s' % (uri.scheme, ', '.join(sorted(self.allowed_schemes)))) if not self.allow_userinfo and uri.userinfo is not None: raise LaunchpadValidationError( 'A username may not be specified in the URI.') if not self.allow_port and uri.port is not None: raise LaunchpadValidationError( 'Non-default ports are not allowed.') if not self.allow_query and uri.query is not None: raise LaunchpadValidationError( 'URIs with query strings are not allowed.') if not self.allow_fragment and uri.fragment is not None: raise LaunchpadValidationError( 'URIs with fragment identifiers are not allowed.') super(URIField, self)._validate(value)
def channels_validator(channels): """Return True if the channels in a list are valid, or raise a LaunchpadValidationError. """ tracks = set() branches = set() for name in channels: try: track, risk, branch = split_channel_name(name) except ValueError: message = _( "Invalid channel name '${name}'. Channel names must be of the " "form 'track/risk/branch', 'track/risk', 'risk/branch', or " "'risk'.", mapping={'name': html_escape(name)}) raise LaunchpadValidationError(structured(message)) tracks.add(track) branches.add(branch) # XXX cjwatson 2018-05-08: These are slightly arbitrary restrictions, # but they make the UI much simpler. if len(tracks) != 1: message = _("Channels must belong to the same track.") raise LaunchpadValidationError(structured(message)) if len(branches) != 1: message = _("Channels must belong to the same branch.") raise LaunchpadValidationError(structured(message)) return True
def _valid_image(self, image): """Check that the given image is under the given constraints.""" # No global import to avoid hard dependency on PIL being installed import PIL.Image if len(image) > self.max_size: raise LaunchpadValidationError(_(dedent(""" This image exceeds the maximum allowed size in bytes."""))) try: pil_image = PIL.Image.open(StringIO(image)) except (IOError, ValueError): raise LaunchpadValidationError(_(dedent(""" The file uploaded was not recognized as an image; please check it and retry."""))) width, height = pil_image.size required_width, required_height = self.dimensions if self.exact_dimensions: if width != required_width or height != required_height: raise LaunchpadValidationError(_(dedent(""" This image is not exactly ${width}x${height} pixels in size."""), mapping={'width': required_width, 'height': required_height})) else: if width > required_width or height > required_height: raise LaunchpadValidationError(_(dedent(""" This image is larger than ${width}x${height} pixels in size."""), mapping={'width': required_width, 'height': required_height})) return True
def getInputValue(self): """See zope.formlib.interfaces.IInputWidget.""" self.setUpSubWidgets() form_value = self.request.form_ng.getOne(self.name) if form_value == 'product': try: return self.product_widget.getInputValue() except MissingInputError: self._error = WidgetInputError( self.name, self.label, LaunchpadValidationError('Please enter a project name')) raise self._error except ConversionError: entered_name = self.request.form_ng.getOne("%s.product" % self.name) self._error = WidgetInputError( self.name, self.label, LaunchpadValidationError( "There is no project named '%s' registered in" " Launchpad" % entered_name)) raise self._error elif form_value == 'package': try: distribution = self.distribution_widget.getInputValue() except ConversionError: entered_name = self.request.form_ng.getOne("%s.distribution" % self.name) self._error = WidgetInputError( self.name, self.label, LaunchpadValidationError( "There is no distribution named '%s' registered in" " Launchpad" % entered_name)) raise self._error if self.package_widget.hasInput(): try: package_name = self.package_widget.getInputValue() if package_name is None: return distribution if IDistributionSourcePackage.providedBy(package_name): dsp = package_name else: source_name = ( distribution.guessPublishedSourcePackageName( package_name.name)) dsp = distribution.getSourcePackage(source_name) except (ConversionError, NotFoundError): entered_name = self.request.form_ng.getOne('%s.package' % self.name) self._error = WidgetInputError( self.name, self.label, LaunchpadValidationError( "There is no package named '%s' published in %s." % (entered_name, distribution.displayname))) raise self._error return dsp else: return distribution else: raise UnexpectedFormData("No valid option was selected.")
def validate_cvs_module(cvsmodule): valid_module = re.compile('^[a-zA-Z][a-zA-Z0-9_/.+-]*$') if not valid_module.match(cvsmodule): raise LaunchpadValidationError( 'The CVS module contains illegal characters.') if cvsmodule == 'CVS': raise LaunchpadValidationError('A CVS module can not be called "CVS".') return True
def validate_cvs_root(cvsroot): try: root = CVSRoot(cvsroot) except CvsRootError as e: raise LaunchpadValidationError(e) if root.method == 'local': raise LaunchpadValidationError('Local CVS roots are not allowed.') if not root.hostname: raise LaunchpadValidationError('CVS root is invalid.') if root.hostname.count('.') == 0: raise LaunchpadValidationError( 'Please use a fully qualified host name.') return True
def file_size_constraint(value, max_size): """Check constraints. The file cannot be empty and must be <= max_size. """ size = len(value) if size == 0: raise LaunchpadValidationError(u'Cannot upload empty file.') elif max_size > 0 and size > max_size: raise LaunchpadValidationError( u'Cannot upload files larger than %i bytes' % max_size) else: return True
def attachment_size_constraint(value): """Constraint for an attachment's file size. The file is not allowed to be empty. """ size = len(value) max_size = config.launchpad.max_attachment_size if size == 0: raise LaunchpadValidationError(u'Cannot upload empty file.') elif max_size > 0 and size > max_size: raise LaunchpadValidationError( u'Cannot upload files larger than %i bytes' % max_size) else: return True
def _validate(self, version): """See `UniqueField`.""" super(DistroSeriesVersionField, self)._validate(version) if not sane_version(version): raise LaunchpadValidationError("%s is not a valid version" % version) # Avoid circular import hell. from lp.archivepublisher.debversion import Version, VersionError try: # XXX sinzui 2009-07-25 bug=404613: DistributionMirror and buildd # have stricter version rules than the schema. The version must # be a debversion. Version(version) except VersionError as error: raise LaunchpadValidationError("'%s': %s" % (version, error))
def _validate(self, value): # import here to avoid circular import from lp.services.webapp import canonical_url from lazr.uri import URI super(DistroMirrorURIField, self)._validate(value) uri = URI(self.normalize(value)) # This field is also used when creating new mirrors and in that case # self.context is not an IDistributionMirror so it doesn't make sense # to try to get the existing value of the attribute. if IDistributionMirror.providedBy(self.context): orig_value = self.get(self.context) if orig_value is not None and URI(orig_value) == uri: return # url was not changed mirror = self.getMirrorByURI(str(uri)) if mirror is not None: message = _( 'The distribution mirror <a href="${url}">${mirror}</a> ' 'is already registered with this URL.', mapping={ 'url': html_escape(canonical_url(mirror)), 'mirror': html_escape(mirror.title) }) raise LaunchpadValidationError(structured(message))
def valid_webref(web_ref): """Returns True if web_ref is a valid download URL, or raises a LaunchpadValidationError. >>> valid_webref('http://example.com') True >>> valid_webref('https://example.com/foo/bar') True >>> valid_webref('ftp://example.com/~ming') True >>> valid_webref('sftp://example.com//absolute/path/maybe') True >>> valid_webref('other://example.com/moo') Traceback (most recent call last): ... LaunchpadValidationError: ... """ if validate_url(web_ref, ['http', 'https', 'ftp', 'sftp']): # Allow ftp so valid_webref can be used for download_url, and so # it doesn't lock out weird projects where the site or # screenshots are kept on ftp. return True else: raise LaunchpadValidationError(_(dedent(""" Not a valid URL. Please enter the full URL, including the scheme (for instance, http:// for a web URL), and ensure the URL uses either http, https or ftp.""")))
def _validate(self, input): """Make sure all the aliases are valid for the field's pillar. An alias is valid if it can be used as the name of a pillar and is not identical to the pillar's existing name. """ context = self.context from lp.registry.interfaces.product import IProduct from lp.registry.interfaces.projectgroup import IProjectGroup from lp.registry.interfaces.distribution import IDistribution if IProduct.providedBy(context): name_field = IProduct['name'] elif IProjectGroup.providedBy(context): name_field = IProjectGroup['name'] elif IDistribution.providedBy(context): name_field = IDistribution['name'] else: raise AssertionError("Unexpected context type.") name_field.bind(context) existing_aliases = context.aliases for name in self._split_input(input): if name == context.name: raise LaunchpadValidationError('This is your name: %s' % name) elif name in existing_aliases: # This is already an alias to this pillar, so there's no need # to validate it. pass else: name_field._validate(name)
def _validate(self, input): """Check that the URL is not already in use by another bugtracker.""" super(BugTrackerURL, self)._validate(input) bugtracker = getUtility(IBugTrackerSet).queryByBaseURL(input) if bugtracker is not None and bugtracker != self.context: raise LaunchpadValidationError( '%s is already registered in Launchpad as "%s" (%s).' % (input, bugtracker.title, bugtracker.name))
def valid_cve_sequence(value): """Check if the given value is a valid CVE otherwise raise an exception. """ if valid_cve(value): return True else: raise LaunchpadValidationError( _("${cve} is not a valid CVE number", mapping={'cve': value}))
def builder_url_validator(url): """Return True if the url is valid, or raise a LaunchpadValidationError""" if not valid_builder_url(url): raise LaunchpadValidationError(_(dedent(""" Invalid builder url '${url}'. Builder urls must be http://host/ or http://host:port/ only. """), mapping={'url': url})) return True
def validate_release_glob(value): """Validate that the URL is supported.""" parts = urlparse(value) if (validate_url(value, ["http", "https", "ftp"]) and '*' in parts[2]): # The product release finder does support the url scheme and # can match more than one file to the url's path part. return True else: raise LaunchpadValidationError('Invalid release URL pattern.')
def getInputValue(self): self._error = None action = self.action_widget.getInputValue() form = self.request.form_ng if action == 'change' and not form.getOne(self.image_widget.name): self._error = WidgetInputError( self.name, self.label, LaunchpadValidationError( _('Please specify the image you want to use.'))) raise self._error if action == "keep": if self.style == self.ADD_STYLE: # It doesn't make any sense to return KEEP_SAME_IMAGE in this # case, since there's nothing to keep. return None elif self.style == self.EDIT_STYLE: return KEEP_SAME_IMAGE else: raise AssertionError( "Style must be one of EDIT_STYLE or ADD_STYLE, got %s" % self.style) elif action == "change": self._image = form.getOne(self.image_widget.name) try: self.context.validate(self._image) except ValidationError as v: self._error = WidgetInputError(self.name, self.label, v) raise self._error self._image.seek(0) content = self._image.read() filename = self._image.filename type, dummy = guess_content_type(name=filename, body=content) # This method may be called more than once in a single request. If # that's the case here we'll simply return the cached # LibraryFileAlias we already have. existing_alias = self._image_file_alias if existing_alias is not None: assert existing_alias.filename == filename, ( "The existing LibraryFileAlias' name doesn't match the " "given image's name.") assert existing_alias.content.filesize == len(content), ( "The existing LibraryFileAlias' size doesn't match " "the given image's size.") assert existing_alias.mimetype == type, ( "The existing LibraryFileAlias' type doesn't match " "the given image's type.") return existing_alias self._image_file_alias = getUtility(ILibraryFileAliasSet).create( name=filename, size=len(content), file=StringIO(content), contentType=type) return self._image_file_alias elif action == "delete": return None
def test_getInputValue_person_missing(self): # An error is raised when the person field is missing. form = self.form form["field.grantee"] = "person" del form["field.grantee.person"] self.widget.request = LaunchpadTestRequest(form=form) message = "Please enter a person or team name" e = self.assertRaises(WidgetInputError, self.widget.getInputValue) self.assertEqual(LaunchpadValidationError(message), e.errors)
def parseLine(self, line): workitem_match = WORKITEM_RE.search(line) if workitem_match: assignee = workitem_match.group('assignee') title = workitem_match.group('title') status = workitem_match.group('status') else: raise LaunchpadValidationError( 'Invalid work item format: "%s"' % line) if title == '': raise LaunchpadValidationError( 'No work item title found on "%s"' % line) if title.startswith('['): raise LaunchpadValidationError( 'Missing closing "]" for assignee on "%s".' % line) return {'title': title, 'status': status.strip().upper(), 'assignee': assignee}
def getAssignee(self, assignee_name): if assignee_name is None: return None from lp.registry.interfaces.person import IPersonSet assignee = getUtility(IPersonSet).getByName(assignee_name) if assignee is None: raise LaunchpadValidationError( "Unknown person name: %s" % assignee_name) return assignee
def getInputValue(self): """See `IInputWidget`.""" self.setUpSubWidgets() track = self.track_widget.getInputValue() risks = self.risks_widget.getInputValue() branch = self.branch_widget.getInputValue() if track and self._separator in track: error_msg = "Track name cannot include '%s'." % self._separator raise WidgetInputError(self.name, self.label, LaunchpadValidationError(error_msg)) if branch and self._separator in branch: error_msg = "Branch name cannot include '%s'." % self._separator raise WidgetInputError(self.name, self.label, LaunchpadValidationError(error_msg)) channels = [ self.buildChannelName(track, risk, branch) for risk in risks ] return channels
def valid_bug_number(value): from lp.bugs.interfaces.bug import IBugSet bugset = getUtility(IBugSet) try: bugset.get(value) except NotFoundError: raise LaunchpadValidationError( _("Bug ${bugid} doesn't exist.", mapping={'bugid': value})) return True
def getInputValue(self): """See `IInputWidget`.""" self.setUpSubWidgets() try: repository = self.repository_widget.getInputValue() except MissingInputError: if self.context.required: raise WidgetInputError( self.name, self.label, LaunchpadValidationError( "Please choose a Git repository.")) else: return None except ConversionError: entered_name = self.request.form_ng.getOne("%s.repository" % self.name) raise WidgetInputError( self.name, self.label, LaunchpadValidationError( "There is no Git repository named '%s' registered in " "Launchpad." % entered_name)) if self.path_widget.hasInput(): path = self.path_widget.getInputValue() else: path = None if not path: if self.context.required: raise WidgetInputError( self.name, self.label, LaunchpadValidationError( "Please enter a Git branch path.")) else: return if self.allow_external and not IGitRepository.providedBy(repository): ref = getUtility(IGitRefRemoteSet).new(repository, path) else: ref = repository.getRefByPath(path) if ref is None: raise WidgetInputError( self.name, self.label, LaunchpadValidationError( "The repository at %s does not contain a branch named " "'%s'." % (repository.display_name, path))) return ref
def valid_remote_bug_url(value): """Verify that the URL is to a bug to a known bug tracker.""" try: getUtility(IBugWatchSet).extractBugTrackerAndBug(value) except NoBugTrackerFound: pass except UnrecognizedBugTrackerURL: raise LaunchpadValidationError( "Launchpad does not recognize the bug tracker at this URL.") return True
def test_getInputValue_product_missing(self): # An error is raised when the product field is missing. form = self.form form['field.target'] = 'product' del form['field.target.product'] self.widget.request = LaunchpadTestRequest(form=form) message = 'Please enter a project name' e = self.assertRaises(WidgetInputError, self.widget.getInputValue) self.assertEqual(LaunchpadValidationError(message), e.errors) self.assertEqual(message, self.widget.error())
def test_getInputValue_package_invalid(self): # An error is raised when the package is not published in the distro. form = self.form form['field.target.package'] = 'non-existent' self.widget.request = LaunchpadTestRequest(form=form) message = ( "There is no package named 'non-existent' published in Fnord.") e = self.assertRaises(WidgetInputError, self.widget.getInputValue) self.assertEqual(LaunchpadValidationError(message), e.errors) self.assertEqual(html_escape(message), self.widget.error())
def test_getInputValue_project_invalid(self): # An error is raised when the project is not valid. form = self.form form["field.target"] = "project" form["field.target.project"] = "non-existent" self.widget.request = LaunchpadTestRequest(form=form) message = ("There is no project named 'non-existent' registered in " "Launchpad") e = self.assertRaises(WidgetInputError, self.widget.getInputValue) self.assertEqual(LaunchpadValidationError(message), e.errors) self.assertEqual(html_escape(message), self.widget.error())
def git_repository_name_validator(name): """Return True if the name is valid, or raise a LaunchpadValidationError. """ if not valid_git_repository_name(name): raise LaunchpadValidationError( _("Invalid Git repository name '${name}'. ${message}", mapping={ "name": name, "message": GIT_REPOSITORY_NAME_VALIDATION_ERROR_MESSAGE, })) return True
def __init__(self, name): self.name = name LaunchpadValidationError.__init__( self, "Invalid name for product: %s." % (name, ))