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: