{% extends "base.html" %} {% block title %}{{ t('profile.user_title', "{username}'s Profile", username=user.display_name or user.username) }} - {{ config.BBS_NAME }}{% endblock %} {% block content %}
{% if avatar %} {{ t('profile.avatar', 'Avatar') }} {% else %} {% endif %}

{{ user.display_name or user.username }}

{% if user.display_name %}

@{{ user.username }}

{% endif %} {% if online %} {{ t('profile.online', 'Online') }} {% else %} {{ t('profile.offline', 'Offline') }} {% endif %} {% if user.is_admin %}
{{ t('profile.admin', 'Admin') }}
{% endif %}
{{ t('profile.user_number', 'User #{id}', id=user.id) }}
{{ t('profile.info', 'Info') }}
{% if field_config.location and user.location %}

{{ user.location }}

{% endif %} {% if field_config.website and user.website %} {# Security fix (templates audit): user.website is a free-text profile field with no scheme validation on save (web/profile.py). Rendering it straight into href= let a user set it to a javascript: URI, which would execute in any other visitor's session when they clicked the "website" link on that profile page -- the same class of bug as the gemini/RSS javascript: URI fix elsewhere in this app. Only link-ify it when it's actually an http(s) URL. #} {% set _site = user.website.strip() %}

{% if _site.lower().startswith('http://') or _site.lower().startswith('https://') %} {{ _site }} {% else %} {{ _site }} {% endif %}

{% endif %} {% if field_config.show_email and user.show_email %}

{{ user.email }}

{% endif %}

{{ t('profile.joined', 'Joined') }} {{ user.created_at|eastern('%Y-%m-%d') }}

{% if user.last_login %}

{{ t('profile.last_login', 'Last login') }} {{ user.last_login|eastern('%Y-%m-%d') }}

{% endif %} {% for cf in custom_fields_list %} {% set val = custom_values.get(cf.id) %} {% if val %}

{% if cf.field_type == 'url' %} {# Same javascript: URI risk as user.website above -- custom "url" fields are also free text with no scheme validation on save. #} {% set _val = (val|string).strip() %} {{ cf.label }}: {% if _val.lower().startswith('http://') or _val.lower().startswith('https://') %} {{ _val }} {% else %} {{ _val }} {% endif %} {% else %} {{ cf.label }}: {{ val }} {% endif %}

{% endif %} {% endfor %}
{{ t('profile.stats', 'Stats') }}

{{ t('profile.posts', 'Posts:') }} {{ stats.total_posts }}

{{ t('profile.replies', 'Replies:') }} {{ stats.total_replies }}

{{ t('profile.member_days', 'Member for: {days} days', days=stats.account_age) }}

{% if current_user.is_authenticated and current_user.username == user.username %} {% elif current_user.is_authenticated %} {% endif %}
{% if field_config.bio and user.bio %}
{{ t('profile.about', 'About') }}

{{ user.bio }}

{% endif %} {% if field_config.signature and user.signature %}
{{ t('profile.signature', 'Signature') }}
{{ user.signature }}
{% endif %} {% if groups %}
{{ t('profile.groups', 'Groups') }} ({{ groups|length }})
{% endif %} {% if achievements %}
{{ t('profile.achievements', 'Achievements') }} ({{ achievements|length }})
{% for a in achievements %}
{{ a.name }}
{{ a.description }}
{{ t('profile.earned', 'earned') }} {{ a.earned_at|eastern('%Y-%m-%d') }}
{% endfor %}
{% endif %}
{{ t('profile.recent_posts', 'Recent Posts') }}
{% if recent_posts %} {% else %}

{{ t('profile.no_posts', 'No posts yet.') }}

{% endif %}
{% endblock %}