diff --git a/qgis-app/plugins/tests/test_validator.py b/qgis-app/plugins/tests/test_validator.py index bfde21af..641870e9 100644 --- a/qgis-app/plugins/tests/test_validator.py +++ b/qgis-app/plugins/tests/test_validator.py @@ -18,8 +18,10 @@ def setUp(self): ) web_not_exist_plugins = os.path.join(TESTFILE_DIR, "web_not_exist.zip") valid_plugins = os.path.join(TESTFILE_DIR, "valid_metadata_link.zip") + invalid_metadata_multiline = os.path.join(TESTFILE_DIR, "invalid_metadata_multiline.zip_") self.valid_metadata_link = open(valid_plugins, "rb") self.invalid_metadata_link = open(invalid_plugins, "rb") + self.invalid_metadata_multiline = open(invalid_metadata_multiline, "rb") self.web_not_exist = open(web_not_exist_plugins, "rb") self.invalid_url_scheme = open(invalid_url_scheme_plugins, "rb") @@ -66,6 +68,27 @@ def test_invalid_metadata_link_tracker_repo_homepage(self): ), ) + def test_invalid_metadata_multiline_attribute(self): + """ + The invalid_metadata_multiline.zip contains metadata file with + a multiline attribute that is commented but still has value. + The parser will append the value of the attribute to the previous + one and make it invalid. + """ + + self.assertRaises( + ValidationError, + validator, + InMemoryUploadedFile( + self.invalid_metadata_multiline, + field_name="tempfile", + name="testfile.zip", + content_type="application/zip", + size=39889, + charset="utf8", + ), + ) + def test_invalid_metadata_url_scheme(self): """ The invalid_url_scheme.zip contains metadata file with diff --git a/qgis-app/plugins/tests/testfiles/invalid_metadata_multiline.zip_ b/qgis-app/plugins/tests/testfiles/invalid_metadata_multiline.zip_ new file mode 100644 index 00000000..afcd4fb5 Binary files /dev/null and b/qgis-app/plugins/tests/testfiles/invalid_metadata_multiline.zip_ differ diff --git a/qgis-app/plugins/validator.py b/qgis-app/plugins/validator.py index e75d9755..ebb3a34c 100644 --- a/qgis-app/plugins/validator.py +++ b/qgis-app/plugins/validator.py @@ -96,6 +96,28 @@ def _check_required_metadata(metadata): ) ) +def _check_multiline_metadata(metadata): + """ + Only the fields 'description', 'about' and 'changelog' can be multiline + If other fields are multiline, raise ValidationError + """ + allowed_multiline_fields = ['description', 'about', 'changelog'] + multiline_fields = [ + field for field in [item[0] for item in metadata] + if field not in ['description', 'about', 'changelog'] and '\n' in dict(metadata).get(field, '') + ] + if len(multiline_fields) > 0: + multiline_fields_str = ', '.join(multiline_fields) + allowed_multiline_fields_str = ', '.join(allowed_multiline_fields) + raise ValidationError( + _( + f'The following metadata fields must be single-line: {multiline_fields_str}. ' + 'Please ensure these fields contain single-line values only. Note that multiline values can also occur if an allowed ' + f'multiline field ({allowed_multiline_fields_str}) is commented out but still contains a multiline value.' + ) + ) + + def _check_url_link(urls): """ Checks if all the url link is valid. @@ -293,6 +315,7 @@ def validator(package, is_new: bool = False): metadata.append(("metadata_source", "__init__.py")) _check_required_metadata(metadata) + _check_multiline_metadata(metadata) # Process Icon try: @@ -375,7 +398,6 @@ def validator(package, is_new: bool = False): if "author" in dict(metadata): if not re.match(r"^[^/]+$", dict(metadata)["author"]): raise ValidationError(_("Author name cannot contain slashes.")) - # strip and check checked_metadata = [] for k, v in metadata: