None:
self.add_data()
@@ -202,11 +202,9 @@ def add_reference_tables_data(self) -> None:
domain = link_.domain
data = get_base_table_data(domain)
if domain.class_.view == 'file':
- extension = data[3]
- data.append(
- profile_image_table_link(entity, domain, extension))
- if not entity.image_id \
- and extension in app.config['DISPLAY_FILE_EXTENSIONS']:
+ ext = data[3]
+ data.append(profile_image_table_link(entity, domain, ext))
+ if not entity.image_id and ext in g.display_file_ext:
entity.image_id = domain.id
elif domain.class_.view != 'source':
data.append(link_.description)
@@ -382,17 +380,15 @@ def add_tabs(self) -> None:
domain = link_.domain
data = get_base_table_data(domain)
if domain.class_.view == 'file':
- extension = data[3]
- data.append(
- profile_image_table_link(entity, domain, extension))
- if not entity.image_id \
- and extension in app.config['DISPLAY_FILE_EXTENSIONS']:
+ ext = data[3]
+ data.append(profile_image_table_link(entity, domain, ext))
+ if not entity.image_id and ext in g.display_file_ext:
entity.image_id = domain.id
if entity.class_.view == 'place' \
and is_authorized('editor') \
and current_user.settings['module_map_overlay']:
content = ''
- if extension in app.config['DISPLAY_FILE_EXTENSIONS']:
+ if ext in app.config['DISPLAY_FILE_EXT']:
overlays = Overlay.get_by_object(entity)
if domain.id in overlays and (html_link := edit_link(
url_for(
diff --git a/openatlas/display/display.py b/openatlas/display/display.py
index 89d12c486..e5ce54fef 100644
--- a/openatlas/display/display.py
+++ b/openatlas/display/display.py
@@ -5,6 +5,7 @@
from flask import g, url_for
from flask_babel import lazy_gettext as _
+from openatlas import app
from openatlas.display.base_display import (
ActorDisplay, BaseDisplay, EventsDisplay, PlaceBaseDisplay,
ReferenceBaseDisplay, TypeBaseDisplay)
@@ -12,7 +13,8 @@
from openatlas.display.table import Table
from openatlas.display.util import (
button, description, edit_link, format_entity_date, get_base_table_data,
- get_file_path, is_authorized, link, remove_link, uc_first)
+ get_file_path, is_authorized, link, remove_link, uc_first,
+ check_iiif_activation, check_iiif_file_exist)
from openatlas.models.entity import Entity
from openatlas.views.tools import carbon_result, sex_result
@@ -66,13 +68,24 @@ class FileDisplay(BaseDisplay):
def add_data(self) -> None:
super().add_data()
self.data[_('size')] = self.entity.get_file_size()
- self.data[_('extension')] = self.entity.get_file_extension()
+ self.data[_('extension')] = self.entity.get_file_ext()
def add_button_others(self) -> None:
if path := get_file_path(self.entity.id):
self.buttons.append(button(
_('download'),
url_for('download_file', filename=path.name)))
+ if check_iiif_activation() \
+ and self.entity.get_file_ext() in g.display_file_ext:
+ if check_iiif_file_exist(self.entity.id) \
+ or not app.config['IIIF']['conversion']:
+ self.buttons.append(button(
+ _('view in IIIF'),
+ url_for('view_iiif', id_=self.entity.id)))
+ else:
+ self.buttons.append(button(
+ _('enable IIIF view'),
+ url_for('make_iiif_available', id_=self.entity.id)))
return
self.buttons.append(
'' + uc_first(_("missing file")) + '')
diff --git a/openatlas/display/image_processing.py b/openatlas/display/image_processing.py
index 6021960b5..748951037 100644
--- a/openatlas/display/image_processing.py
+++ b/openatlas/display/image_processing.py
@@ -8,13 +8,11 @@
def resize_image(filename: str) -> None:
file_format = '.' + filename.split('.', 1)[1].lower()
- if file_format in app.config['ALLOWED_IMAGE_EXT']:
- loop_resize_image(filename.rsplit('.', 1)[0].lower(), file_format)
-
-
-def loop_resize_image(name: str, file_format: str) -> None:
- for size in app.config['IMAGE_SIZE'].values():
- safe_resize_image(name, file_format, size)
+ if file_format in g.display_file_ext:
+ for size in app.config['IMAGE_SIZE'].values():
+ safe_resize_image(
+ filename.rsplit('.', 1)[0].lower(),
+ file_format, size)
def safe_resize_image(name: str, file_format: str, size: str) -> bool:
@@ -32,23 +30,23 @@ def safe_resize_image(name: str, file_format: str, size: str) -> bool:
def image_resizing(name: str, format_: str, size: str) -> bool:
- conf = app.config
- filename = Path(conf['UPLOAD_PATH']) / f"{name}{format_}[0]"
+ filename = Path(app.config['UPLOAD_PATH']) / f"{name}{format_}[0]"
with Image(filename=filename) as src:
- ext = conf['PROCESSED_EXT'] \
- if format_ in conf['NONE_DISPLAY_EXT'] else format_
- with src.convert(ext.replace('.', '')) as img:
+ if format_ in app.config['PROCESSABLE_EXT']:
+ format_ = app.config['PROCESSED_EXT'] # pragma: no cover
+ with src.convert(format_.replace('.', '')) as img:
img.transform(resize=f"{size}x{size}>")
img.compression_quality = 75
img.save(
- filename=Path(conf['RESIZED_IMAGES']) / size / f"{name}{ext}")
+ filename=Path(
+ app.config['RESIZED_IMAGES']) / size / f"{name}{format_}")
return True
def check_processed_image(filename: str) -> bool:
file_format = '.' + filename.split('.', 1)[1].lower()
try:
- if file_format in app.config['ALLOWED_IMAGE_EXT']:
+ if file_format in g.display_file_ext:
return loop_through_processed_folders(
filename.rsplit('.', 1)[0].lower(),
file_format)
@@ -62,8 +60,9 @@ def check_processed_image(filename: str) -> bool:
def loop_through_processed_folders(name: str, file_format: str) -> bool:
- ext = app.config['PROCESSED_EXT'] \
- if file_format in app.config['NONE_DISPLAY_EXT'] else file_format
+ ext = file_format
+ if file_format in app.config['PROCESSABLE_EXT']:
+ ext = app.config['PROCESSED_EXT'] # pragma: no cover
for size in app.config['IMAGE_SIZE'].values():
path = Path(app.config['RESIZED_IMAGES']) / size / f"{name}{ext}"
if not path.is_file() \
@@ -97,7 +96,6 @@ def delete_orphaned_resized_images() -> None:
def create_resized_images() -> None:
from openatlas.models.entity import Entity
- for entity in Entity.get_by_class('file'):
- if entity.id in g.files:
- if entity.get_file_extension() in app.config['ALLOWED_IMAGE_EXT']:
- resize_image(f"{entity.id}{entity.get_file_extension()}")
+ for e in Entity.get_by_class('file'):
+ if e.id in g.files and e.get_file_ext() in g.display_file_ext:
+ resize_image(f"{e.id}{e.get_file_ext()}")
diff --git a/openatlas/display/tab.py b/openatlas/display/tab.py
index 246430f55..6dfb4cd9e 100644
--- a/openatlas/display/tab.py
+++ b/openatlas/display/tab.py
@@ -118,12 +118,12 @@ def set_buttons(self, name: str, entity: Optional[Entity] = None) -> None:
g.classes[item].label,
url_for('insert', class_=item, origin_id=id_)))
elif name == 'artifact':
- if (entity and entity.class_.name in
+ if entity and entity.class_.name in \
['place',
'artifact',
'human_remains',
'feature',
- 'stratigraphic_unit']):
+ 'stratigraphic_unit']:
self.buttons.append(
button(_('add subunit'),
url_for('add_subunit', super_id=id_)))
diff --git a/openatlas/display/util.py b/openatlas/display/util.py
index 97ce708d7..9a1a7b89c 100644
--- a/openatlas/display/util.py
+++ b/openatlas/display/util.py
@@ -4,6 +4,7 @@
import os
import re
import smtplib
+import subprocess
from datetime import datetime, timedelta
from email.header import Header
from email.mime.text import MIMEText
@@ -65,8 +66,8 @@ def ext_references(links: list[Link]) -> str:
f'{system.resolver_url}{link_.description}',
external=True) if system.resolver_url else link_.description
html += \
- f' ({ g.types[link_.type.id].name } ' + _('at') + \
- f' { link(link_.domain) })
'
+ f' ({g.types[link_.type.id].name} ' + _('at') + \
+ f' {link(link_.domain)})
'
return html
@@ -127,17 +128,12 @@ def format_entity_date(
return html + (f" ({comment})" if comment else '')
-def profile_image_table_link(
- entity: Entity,
- file: Entity,
- extension: str) -> str:
+def profile_image_table_link(entity: Entity, file: Entity, ext: str) -> str:
if file.id == entity.image_id:
return link(
_('unset'),
url_for('file_remove_profile_image', entity_id=entity.id))
- if extension in app.config['DISPLAY_FILE_EXTENSIONS'] or (
- g.settings['image_processing']
- and extension in app.config['ALLOWED_IMAGE_EXT']):
+ if ext in g.display_file_ext:
return link(
_('set'),
url_for('set_profile_image', id_=file.id, origin_id=entity.id))
@@ -231,40 +227,45 @@ def display_menu(entity: Optional[Entity], origin: Optional[Entity]) -> str:
def profile_image(entity: Entity) -> str:
if not entity.image_id:
return ''
- path = get_file_path(entity.image_id)
- if not path:
+ if not (path := get_file_path(entity.image_id)):
return '' # pragma: no cover
- resized = None
- size = app.config['IMAGE_SIZE']['thumbnail']
- if g.settings['image_processing'] and check_processed_image(path.name):
- if path_ := get_file_path(entity.image_id, size):
- resized = url_for('display_file', filename=path_.name, size=size)
- url = url_for('display_file', filename=path.name)
- src = resized or url
- style = f'max-width:{g.settings["profile_image_width"]}px;'
- ext = app.config["DISPLAY_FILE_EXTENSIONS"]
- if resized:
- style = f'max-width:{app.config["IMAGE_SIZE"]["thumbnail"]}px;'
- ext = app.config["ALLOWED_IMAGE_EXT"]
+
+ src = url_for('display_file', filename=path.name)
+ url = src
+ width = g.settings["profile_image_width"]
+ if app.config['IIIF']['enabled'] and check_iiif_file_exist(entity.id):
+ url = url_for('view_iiif', id_=entity.id)
+ iiif_ext = '.tiff' if app.config['IIIF']['conversion'] \
+ else g.files[entity.id].suffix
+ src = \
+ f"{app.config['IIIF']['url']}{entity.id}{iiif_ext}" \
+ f"/full/!{width},{width}/0/default.jpg"
+ elif g.settings['image_processing'] and check_processed_image(path.name):
+ if path_ := get_file_path(
+ entity.image_id,
+ app.config['IMAGE_SIZE']['thumbnail']):
+ src = url_for(
+ 'display_file',
+ size=app.config['IMAGE_SIZE']['thumbnail'],
+ filename=path_.name)
+ external = False
if entity.class_.view == 'file':
- html = \
- '' + _('no preview available') + '
'
- if path.suffix.lower() in ext:
- html = link(
- f'',
- url,
- external=True)
+ external = True
+ if path.suffix.lower() not in g.display_file_ext:
+ return '' + _('no preview available') + '
'
else:
- html = link(
- f'',
- url_for('view', id_=entity.image_id))
- return f'{html}'
+ url = url_for('view', id_=entity.image_id)
+ html = link(
+ f'',
+ url,
+ external=external)
+ return html
@app.template_filter()
def get_js_messages(lang: str) -> str:
js_message_file = Path('static') / 'vendor' / 'jquery_validation_plugin' \
- / f'messages_{lang}.js'
+ / f'messages_{lang}.js'
if not (Path(app.root_path) / js_message_file).is_file():
return ''
return f''
@@ -313,7 +314,7 @@ def get_backup_file_data() -> dict[str, Any]:
latest_file = None
latest_file_date = None
for file in [
- f for f in path.iterdir()
+ f for f in path.iterdir()
if (path / f).is_file() and f.name != '.gitignore']:
file_date = datetime.utcfromtimestamp((path / file).stat().st_ctime)
if not latest_file_date or file_date > latest_file_date:
@@ -339,7 +340,7 @@ def get_base_table_data(entity: Entity, show_links: bool = True) -> list[Any]:
data.append(entity.standard_type.name if entity.standard_type else '')
if entity.class_.name == 'file':
data.append(entity.get_file_size())
- data.append(entity.get_file_extension())
+ data.append(entity.get_file_ext())
if entity.class_.view in ['actor', 'artifact', 'event', 'place']:
data.append(entity.first)
data.append(entity.last)
@@ -426,11 +427,11 @@ def system_warnings(_context: str, _unneeded_string: str) -> str:
warnings.append(
f"Database version {app.config['DATABASE_VERSION']} is needed but "
f"current version is {g.settings['database_version']}")
- for path in app.config['WRITABLE_PATHS']:
- if not os.access(path, os.W_OK):
- warnings.append(
- '' + _('directory not writable') +
- f" {str(path).replace(app.root_path, '')}
")
+ if app.config['IIIF']['enabled']:
+ if path := app.config['IIIF']['path']:
+ check_write_access(path, warnings)
+ for path in g.writable_paths:
+ check_write_access(path, warnings)
if is_authorized('admin'):
from openatlas.models.user import User
user = User.get_by_username('OpenAtlas')
@@ -448,6 +449,14 @@ def system_warnings(_context: str, _unneeded_string: str) -> str:
f'{"
".join(warnings)}' if warnings else ''
+def check_write_access(path: Path, warnings: list[str]) -> list[str]:
+ if not os.access(path, os.W_OK):
+ warnings.append(
+ '' + _('directory not writable') +
+ f" {str(path).replace(app.root_path, '')}
")
+ return warnings
+
+
@app.template_filter()
def tooltip(text: str) -> str:
if not text:
@@ -466,7 +475,7 @@ def get_file_path(
return None
ext = g.files[id_].suffix
if size:
- if ext in app.config['NONE_DISPLAY_EXT']:
+ if ext in app.config['PROCESSABLE_EXT']:
ext = app.config['PROCESSED_EXT'] # pragma: no cover
path = app.config['RESIZED_IMAGES'] / size / f"{id_}{ext}"
return path if os.path.exists(path) else None
@@ -639,8 +648,9 @@ def manual(site: str) -> str:
return ''
return \
'