diff --git a/tom_common/apps.py b/tom_common/apps.py
index b242634f..8eb4ddd9 100644
--- a/tom_common/apps.py
+++ b/tom_common/apps.py
@@ -20,3 +20,15 @@ def ready(self):
plotly_theme = 'plotly_white'
pio.templates.default = plotly_theme
+
+ def profile_details(self):
+ """
+ Integration point for adding items to the user profile page.
+
+ This method should return a list of dictionaries that include a `partial` key pointing to the path of the html
+ profile partial. The `context` key should point to the dot separated string path to the templatetag that will
+ return a dictionary containing new context for the accompanying partial.
+ Typically, this partial will be a bootstrap card displaying some app specific user data.
+ """
+ return [{'partial': 'tom_common/partials/user_data.html',
+ 'context': 'tom_common.templatetags.user_extras.user_data'}]
diff --git a/tom_common/templates/tom_common/partials/profile_app_addons.html b/tom_common/templates/tom_common/partials/profile_app_addons.html
new file mode 100644
index 00000000..910e1284
--- /dev/null
+++ b/tom_common/templates/tom_common/partials/profile_app_addons.html
@@ -0,0 +1,19 @@
+{% load user_extras %}
+{% load bootstrap4 %}
+
+
+
+
+ {% for profile in profile_list %}
+
+ {% include profile %}
+
+ {% if forloop.counter|divisibleby:2 %}
+
+
+ {% endif %}
+ {% empty %}
+ This user has no profile.
+ {% endfor %}
+
+
\ No newline at end of file
diff --git a/tom_common/templates/tom_common/user_profile.html b/tom_common/templates/tom_common/user_profile.html
index 8bb78403..02afdcc2 100644
--- a/tom_common/templates/tom_common/user_profile.html
+++ b/tom_common/templates/tom_common/user_profile.html
@@ -11,14 +11,6 @@
{% endif %}
-
-
-
- {% user_data user %}
-
-
-
-
-
+{% profile_app_addons user %}
{% endblock %}
diff --git a/tom_common/templatetags/user_extras.py b/tom_common/templatetags/user_extras.py
index 8018d7f3..db0b1e3c 100644
--- a/tom_common/templatetags/user_extras.py
+++ b/tom_common/templatetags/user_extras.py
@@ -1,8 +1,12 @@
+import logging
from django import template
from django.contrib.auth.models import Group, User
from django.forms.models import model_to_dict
+from django.apps import apps
+from django.utils.module_loading import import_string
register = template.Library()
+logger = logging.getLogger(__name__)
@register.inclusion_tag('auth/partials/group_list.html', takes_context=True)
@@ -42,3 +46,36 @@ def user_data(user):
'user_data': user_dict,
'profile_data': profile_dict,
}
+
+
+@register.inclusion_tag('tom_common/partials/profile_app_addons.html', takes_context=True)
+def profile_app_addons(context, user):
+ """
+ Imports the profile content from relevant apps.
+
+ Each profile should be contained in a list of dictionaries in an app's apps.py `profile_details` method.
+ Each profile dictionary should contain a 'context' key with the path to the context processor class (typically a
+ templatetag), and a 'partial' key with the path to the html partial template.
+ """
+ partial_list = []
+ for app in apps.get_app_configs():
+ try:
+ profile_details = app.profile_details()
+ if profile_details:
+ for profile in profile_details:
+ try:
+ clazz = import_string(profile['context'])
+ except ImportError:
+ logger.warning(f'WARNING: Could not import context for {app.name} profile from '
+ f'{profile["context"]}.\n'
+ f'Are you sure you have the right path?')
+ continue
+ new_context = clazz(user)
+ for item in new_context:
+ context[item] = new_context[item]
+ partial_list.append(profile['partial'])
+ except AttributeError:
+ pass
+ context['user'] = user
+ context['profile_list'] = partial_list
+ return context