mirror of
https://github.com/inventree/InvenTree.git
synced 2026-04-04 18:40:55 +00:00
Add option to "asset" tag to control error raising (#11591)
This commit is contained in:
@@ -188,20 +188,20 @@ def static_file_exists(path: Path | str) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def get_static_file_contents(
|
def get_static_file_contents(
|
||||||
path: Path | str, raise_error: bool = True
|
path: Path | str, raise_error: bool = False
|
||||||
) -> bytes | None:
|
) -> bytes | None:
|
||||||
"""Return the contents of a static file.
|
"""Return the contents of a static file.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
path: The path to the static file, relative to the static storage root
|
path: The path to the static file, relative to the static storage root
|
||||||
raise_error: If True, raise an error if the file cannot be found (default = True)
|
raise_error: If True, raise an error if the file cannot be found (default = False)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The contents of the static file, or None if the file cannot be found
|
The contents of the static file, or None if the file cannot be found
|
||||||
"""
|
"""
|
||||||
if not path:
|
if not path:
|
||||||
if raise_error:
|
if raise_error:
|
||||||
raise ValueError('No media file specified')
|
raise ValueError('No static file specified')
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -217,12 +217,14 @@ def get_static_file_contents(
|
|||||||
return file_data
|
return file_data
|
||||||
|
|
||||||
|
|
||||||
def get_media_file_contents(path: Path | str, raise_error: bool = True) -> bytes | None:
|
def get_media_file_contents(
|
||||||
|
path: Path | str, raise_error: bool = False
|
||||||
|
) -> bytes | None:
|
||||||
"""Return the fully qualified file path to an uploaded media file.
|
"""Return the fully qualified file path to an uploaded media file.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
path: The path to the media file, relative to the media storage root
|
path: The path to the media file, relative to the media storage root
|
||||||
raise_error: If True, raise an error if the file cannot be found (default = True)
|
raise_error: If True, raise an error if the file cannot be found (default = False)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The contents of the media file, or None if the file cannot be found
|
The contents of the media file, or None if the file cannot be found
|
||||||
@@ -255,15 +257,24 @@ def get_media_file_contents(path: Path | str, raise_error: bool = True) -> bytes
|
|||||||
|
|
||||||
|
|
||||||
@register.simple_tag()
|
@register.simple_tag()
|
||||||
def asset(filename):
|
def asset(filename: str, raise_error: bool = False) -> str | None:
|
||||||
"""Return fully-qualified path for an upload report asset file.
|
"""Return fully-qualified path for an upload report asset file.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
filename: Asset filename (relative to the 'assets' media directory)
|
filename: Asset filename (relative to the 'assets' media directory)
|
||||||
|
raise_error: If True, raise an error if the file cannot be found (default = False)
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
FileNotFoundError: If file does not exist
|
FileNotFoundError: If file does not exist
|
||||||
|
ValueError: If an invalid filename is provided (e.g. empty string)
|
||||||
|
ValidationError: If the filename is invalid (e.g. path traversal attempt)
|
||||||
"""
|
"""
|
||||||
|
if not filename:
|
||||||
|
if raise_error:
|
||||||
|
raise ValueError('No asset file specified')
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
if type(filename) is SafeString:
|
if type(filename) is SafeString:
|
||||||
# Prepend an empty string to enforce 'stringiness'
|
# Prepend an empty string to enforce 'stringiness'
|
||||||
filename = '' + filename
|
filename = '' + filename
|
||||||
@@ -274,7 +285,10 @@ def asset(filename):
|
|||||||
full_path = Path('report', 'assets', filename)
|
full_path = Path('report', 'assets', filename)
|
||||||
|
|
||||||
if not media_file_exists(full_path):
|
if not media_file_exists(full_path):
|
||||||
raise FileNotFoundError(_('Asset file not found') + f": '{filename}'")
|
if raise_error:
|
||||||
|
raise FileNotFoundError(_('Asset file not found') + f": '{filename}'")
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
# In debug mode, return a web URL to the asset file (rather than a local file path)
|
# In debug mode, return a web URL to the asset file (rather than a local file path)
|
||||||
if get_global_setting('REPORT_DEBUG_MODE', cache=False):
|
if get_global_setting('REPORT_DEBUG_MODE', cache=False):
|
||||||
@@ -294,6 +308,7 @@ def uploaded_image(
|
|||||||
width: Optional[int] = None,
|
width: Optional[int] = None,
|
||||||
height: Optional[int] = None,
|
height: Optional[int] = None,
|
||||||
rotate: Optional[float] = None,
|
rotate: Optional[float] = None,
|
||||||
|
raise_error: bool = False,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Return raw image data from an 'uploaded' image.
|
"""Return raw image data from an 'uploaded' image.
|
||||||
@@ -306,12 +321,14 @@ def uploaded_image(
|
|||||||
width: Optional width of the image
|
width: Optional width of the image
|
||||||
height: Optional height of the image
|
height: Optional height of the image
|
||||||
rotate: Optional rotation to apply to the image
|
rotate: Optional rotation to apply to the image
|
||||||
|
raise_error: If True, raise an error if the file cannot be found (default = False)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Binary image data to be rendered directly in a <img> tag
|
Binary image data to be rendered directly in a <img> tag
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
FileNotFoundError: If the file does not exist
|
FileNotFoundError: If the file does not exist
|
||||||
|
ValueError: If an invalid filename is provided (e.g. empty string)
|
||||||
"""
|
"""
|
||||||
if type(filename) is SafeString:
|
if type(filename) is SafeString:
|
||||||
# Prepend an empty string to enforce 'stringiness'
|
# Prepend an empty string to enforce 'stringiness'
|
||||||
@@ -330,7 +347,7 @@ def uploaded_image(
|
|||||||
raise FileNotFoundError(_('Image file not found') + f": '{filename}'")
|
raise FileNotFoundError(_('Image file not found') + f": '{filename}'")
|
||||||
|
|
||||||
if exists:
|
if exists:
|
||||||
img_data = get_media_file_contents(filename, raise_error=False)
|
img_data = get_media_file_contents(filename, raise_error=raise_error)
|
||||||
|
|
||||||
# Check if the image data is valid
|
# Check if the image data is valid
|
||||||
if (
|
if (
|
||||||
@@ -344,7 +361,9 @@ def uploaded_image(
|
|||||||
else:
|
else:
|
||||||
# Load the backup image from the static files directory
|
# Load the backup image from the static files directory
|
||||||
replacement_file_path = Path('img', replacement_file)
|
replacement_file_path = Path('img', replacement_file)
|
||||||
img_data = get_static_file_contents(replacement_file_path)
|
img_data = get_static_file_contents(
|
||||||
|
replacement_file_path, raise_error=raise_error
|
||||||
|
)
|
||||||
|
|
||||||
if debug_mode:
|
if debug_mode:
|
||||||
# In debug mode, return a web path (rather than an encoded image blob)
|
# In debug mode, return a web path (rather than an encoded image blob)
|
||||||
|
|||||||
@@ -61,7 +61,15 @@ class ReportTagTest(PartImageTestMixin, InvenTreeTestCase):
|
|||||||
self.debug_mode(b)
|
self.debug_mode(b)
|
||||||
|
|
||||||
with self.assertRaises(FileNotFoundError):
|
with self.assertRaises(FileNotFoundError):
|
||||||
report_tags.asset('bad_file.txt')
|
report_tags.asset('bad_file.txt', raise_error=True)
|
||||||
|
|
||||||
|
# Test for missing file, no error
|
||||||
|
self.assertIsNone(report_tags.asset('missing.txt'))
|
||||||
|
|
||||||
|
self.assertIsNone(report_tags.asset(''))
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
report_tags.asset('', raise_error=True)
|
||||||
|
|
||||||
# Create an asset file
|
# Create an asset file
|
||||||
asset_dir = settings.MEDIA_ROOT.joinpath('report', 'assets')
|
asset_dir = settings.MEDIA_ROOT.joinpath('report', 'assets')
|
||||||
@@ -80,6 +88,7 @@ class ReportTagTest(PartImageTestMixin, InvenTreeTestCase):
|
|||||||
|
|
||||||
self.debug_mode(False)
|
self.debug_mode(False)
|
||||||
asset = report_tags.asset('test.txt')
|
asset = report_tags.asset('test.txt')
|
||||||
|
self.assertEqual(asset, f'file://{settings.MEDIA_ROOT}/report/assets/test.txt')
|
||||||
|
|
||||||
# Test for attempted path traversal
|
# Test for attempted path traversal
|
||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
@@ -92,10 +101,10 @@ class ReportTagTest(PartImageTestMixin, InvenTreeTestCase):
|
|||||||
self.assertFalse(report_tags.static_file_exists(fn))
|
self.assertFalse(report_tags.static_file_exists(fn))
|
||||||
|
|
||||||
with self.assertRaises(FileNotFoundError):
|
with self.assertRaises(FileNotFoundError):
|
||||||
report_tags.get_media_file_contents('dummy_file.txt')
|
report_tags.get_media_file_contents('dummy_file.txt', raise_error=True)
|
||||||
|
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
report_tags.get_static_file_contents(None)
|
report_tags.get_static_file_contents(None, raise_error=True)
|
||||||
|
|
||||||
# Try again, without throwing an error
|
# Try again, without throwing an error
|
||||||
self.assertIsNone(
|
self.assertIsNone(
|
||||||
|
|||||||
Reference in New Issue
Block a user