Fix link issues

This commit is contained in:
Patrick Jentsch 2023-03-22 12:06:33 +01:00
parent 622d32fa45
commit 57813b4bc2
8 changed files with 127 additions and 89 deletions

View File

@ -14,7 +14,7 @@ from app.users.utils import (
@bp.route('') @bp.route('')
@register_breadcrumb(bp, '.', '<i class="material-icons left">admin_panel_settings</i>Administration') @register_breadcrumb(bp, '.', '<i class="material-icons left">admin_panel_settings</i>Administration')
def index(): def admin():
return render_template( return render_template(
'admin/admin.html.j2', 'admin/admin.html.j2',
title='Administration' title='Administration'

View File

@ -100,6 +100,15 @@ class ProfilePrivacySettings(IntEnum):
SHOW_LAST_SEEN = 2 SHOW_LAST_SEEN = 2
SHOW_MEMBER_SINCE = 4 SHOW_MEMBER_SINCE = 4
@staticmethod
def get(profile_privacy_setting: Union['ProfilePrivacySettings', int, str]) -> 'ProfilePrivacySettings':
if isinstance(profile_privacy_setting, ProfilePrivacySettings):
return profile_privacy_setting
if isinstance(profile_privacy_setting, int):
return ProfilePrivacySettings(profile_privacy_setting)
if isinstance(profile_privacy_setting, str):
return ProfilePrivacySettings[profile_privacy_setting]
raise TypeError('profile_privacy_setting must be ProfilePrivacySettings, int, or str')
class CorpusFollowerPermission(IntEnum): class CorpusFollowerPermission(IntEnum):
VIEW = 1 VIEW = 1
@ -227,18 +236,18 @@ class Role(HashidMixin, db.Model):
return f'<Role {self.name}>' return f'<Role {self.name}>'
def has_permission(self, permission: Union[Permission, int, str]): def has_permission(self, permission: Union[Permission, int, str]):
perm = Permission.get(permission) p = Permission.get(permission)
return self.permissions & perm.value == perm.value return self.permissions & p.value == p.value
def add_permission(self, permission: Union[Permission, int, str]): def add_permission(self, permission: Union[Permission, int, str]):
perm = Permission.get(permission) p = Permission.get(permission)
if not self.has_permission(perm): if not self.has_permission(p):
self.permissions += perm.value self.permissions += p.value
def remove_permission(self, permission: Union[Permission, int, str]): def remove_permission(self, permission: Union[Permission, int, str]):
perm = Permission.get(permission) p = Permission.get(permission)
if self.has_permission(perm): if self.has_permission(p):
self.permissions -= perm.value self.permissions -= p.value
def reset_permissions(self): def reset_permissions(self):
self.permissions = 0 self.permissions = 0
@ -763,15 +772,18 @@ class User(HashidMixin, UserMixin, db.Model):
#region Profile Privacy settings #region Profile Privacy settings
def has_profile_privacy_setting(self, setting): def has_profile_privacy_setting(self, setting):
return self.profile_privacy_settings & setting == setting s = ProfilePrivacySettings.get(setting)
return self.profile_privacy_settings & s.value == s.value
def add_profile_privacy_setting(self, setting): def add_profile_privacy_setting(self, setting):
if not self.has_profile_privacy_setting(setting): s = ProfilePrivacySettings.get(setting)
self.profile_privacy_settings += setting if not self.has_profile_privacy_setting(s):
self.profile_privacy_settings += s.value
def remove_profile_privacy_setting(self, setting): def remove_profile_privacy_setting(self, setting):
if self.has_profile_privacy_setting(setting): s = ProfilePrivacySettings.get(setting)
self.profile_privacy_settings -= setting if self.has_profile_privacy_setting(s):
self.profile_privacy_settings -= s.value
def reset_profile_privacy_settings(self): def reset_profile_privacy_settings(self):
self.profile_privacy_settings = 0 self.profile_privacy_settings = 0
@ -838,7 +850,7 @@ class User(HashidMixin, UserMixin, db.Model):
json_serializeable = { json_serializeable = {
'id': self.hashid, 'id': self.hashid,
'confirmed': self.confirmed, 'confirmed': self.confirmed,
'avatar': url_for('users.profile_avatar', user_id=self.id), 'avatar': url_for('users.user_avatar', user_id=self.id),
'email': self.email, 'email': self.email,
'last_seen': ( 'last_seen': (
None if self.last_seen is None None if self.last_seen is None

View File

@ -4,13 +4,9 @@
<div class="background primary-color"></div> <div class="background primary-color"></div>
<div class="row"> <div class="row">
<div class="col s4"> <div class="col s4">
<a href="{{ url_for('users.user', user_id=current_user.id) }}"> <a href="{{ url_for('users.user', user_id=current_user.id) }}">
{% if current_user.avatar %} <img src="{{ url_for('users.user_avatar', user_id=current_user.id) }}" alt="user-image" class="circle responsive-img" style="height:80%; margin-top: 13px; margin-left:-15px;">
<img src="{{ url_for('users.profile_avatar', user_id=current_user.id) }}" alt="user-image" class="circle responsive-img" style="height:80%; margin-top: 13px; margin-left:-15px;"> </a>
{% else %}
<img src="{{ url_for('static', filename='images/user_avatar.png') }}" alt="user-image" class="circle responsive-img" style="height:80%; margin-top: 13px; margin-left:-15px;">
{% endif %}
</a>
</div> </div>
<div class="col s8"> <div class="col s8">
<span class="white-text name">{{ current_user.username }}</span> <span class="white-text name">{{ current_user.username }}</span>
@ -25,39 +21,72 @@
</div> </div>
</li> #} </li> #}
{# <li><a href="{{ url_for('main.index') }}">nopaque</a></li> #} {# <li><a href="{{ url_for('main.index') }}">nopaque</a></li> #}
<li class="hide-on-large-only"><a href="{{ url_for('main.news') }}"><i class="material-icons left">email</i>News</a></li> <li class="hide-on-large-only">
<a class="waves-effect" href="{{ url_for('main.news') }}"><i class="material-icons left">email</i>News</a>
</li>
{# <li><a href="{{ url_for('main.user_manual') }}"><i class="material-icons">help</i>Manual</a></li> #} {# <li><a href="{{ url_for('main.user_manual') }}"><i class="material-icons">help</i>Manual</a></li> #}
<li><a href="{{ url_for('main.dashboard') }}" class="subheader">Dashboard</a></li> <li>
<li><a href="{{ url_for('main.dashboard', _anchor='corpora') }}"><i class="nopaque-icons">I</i>My Corpora</a></li> <a class="waves-effect" class="waves-effect" href="{{ url_for('main.dashboard') }}"><i class="material-icons">dashboard</i>Dashboard</a>
<li><a href="{{ url_for('main.dashboard', _anchor='jobs') }}"><i class="nopaque-icons">J</i>My Jobs</a></li> <ul>
<li><a href="{{ url_for('main.dashboard', _anchor='contributions') }}"><i class="material-icons">new_label</i>My Contributions</a></li> <li>
<a class="waves-effect" href="{{ url_for('main.dashboard', _anchor='corpora') }}" style="padding-left: 47px;"><i class="nopaque-icons">I</i>My Corpora</a>
</li>
<li>
<a class="waves-effect" href="{{ url_for('main.dashboard', _anchor='jobs') }}" style="padding-left: 47px;"><i class="nopaque-icons">J</i>My Jobs</a>
</li>
<li>
<a class="waves-effect" href="{{ url_for('main.dashboard', _anchor='contributions') }}" style="padding-left: 47px;"><i class="material-icons">new_label</i>My Contributions</a>
</li>
</ul>
</li>
<li><div class="divider"></div></li> <li><div class="divider"></div></li>
<li><a class="subheader">Processes & Services</a></li> <li><a class="subheader">Processes & Services</a></li>
<li class="service-color service-color-border border-darken" data-service="file-setup-pipeline" style="border-left: 10px solid; margin-top: 5px;"><a href="{{ url_for('services.file_setup_pipeline') }}"><i class="nopaque-icons service-icons" data-service="file-setup-pipeline"></i>File setup</a></li> <li class="service-color service-color-border border-darken" data-service="file-setup-pipeline" style="border-left: 10px solid; margin-top: 5px;">
<li class="service-color service-color-border border-darken" data-service="tesseract-ocr-pipeline" style="border-left: 10px solid; margin-top: 5px;"><a href="{{ url_for('services.tesseract_ocr_pipeline') }}"><i class="nopaque-icons service-icons" data-service="tesseract-ocr-pipeline"></i>OCR</a></li> <a class="waves-effect" href="{{ url_for('services.file_setup_pipeline') }}"><i class="nopaque-icons service-icons" data-service="file-setup-pipeline"></i>File setup</a>
</li>
<li class="service-color service-color-border border-darken" data-service="tesseract-ocr-pipeline" style="border-left: 10px solid; margin-top: 5px;">
<a class="waves-effect" href="{{ url_for('services.tesseract_ocr_pipeline') }}"><i class="nopaque-icons service-icons" data-service="tesseract-ocr-pipeline"></i>OCR</a>
</li>
{% if config.NOPAQUE_TRANSKRIBUS_ENABLED %} {% if config.NOPAQUE_TRANSKRIBUS_ENABLED %}
<li class="service-color service-color-border border-darken" data-service="transkribus-htr-pipeline" style="border-left: 10px solid; margin-top: 5px;"><a href="{{ url_for('services.transkribus_htr_pipeline') }}"><i class="nopaque-icons service-icons" data-service="transkribus-htr-pipeline"></i>HTR</a></li> <li class="service-color service-color-border border-darken" data-service="transkribus-htr-pipeline" style="border-left: 10px solid; margin-top: 5px;">
<a class="waves-effect" href="{{ url_for('services.transkribus_htr_pipeline') }}"><i class="nopaque-icons service-icons" data-service="transkribus-htr-pipeline"></i>HTR</a>
</li>
{% endif %} {% endif %}
<li class="service-color service-color-border border-darken" data-service="spacy-nlp-pipeline" style="border-left: 10px solid; margin-top: 5px;"><a href="{{ url_for('services.spacy_nlp_pipeline') }}"><i class="nopaque-icons service-icons" data-service="spacy-nlp-pipeline"></i>NLP</a></li> <li class="service-color service-color-border border-darken" data-service="spacy-nlp-pipeline" style="border-left: 10px solid; margin-top: 5px;">
<li class="service-color service-color-border border-darken" data-service="corpus-analysis" style="border-left: 10px solid; margin-top: 5px;"><a href="{{ url_for('services.corpus_analysis') }}"><i class="nopaque-icons service-icons" data-service="corpus-analysis"></i>Corpus Analysis</a></li> <a class="waves-effect" href="{{ url_for('services.spacy_nlp_pipeline') }}"><i class="nopaque-icons service-icons" data-service="spacy-nlp-pipeline"></i>NLP</a>
</li>
<li class="service-color service-color-border border-darken" data-service="corpus-analysis" style="border-left: 10px solid; margin-top: 5px;">
<a class="waves-effect" href="{{ url_for('services.corpus_analysis') }}"><i class="nopaque-icons service-icons" data-service="corpus-analysis"></i>Corpus Analysis</a>
</li>
<li><div class="divider"></div></li> <li><div class="divider"></div></li>
<li><a class="subheader">Social Area</a></li> <li>
<li><a href="{{ url_for('main.social_area', _anchor='public-users') }}"><i class="material-icons">person</i>Public Users</a></li> <a class="waves-effect" class="waves-effect" href="{{ url_for('main.social_area') }}"><i class="material-icons">rocket_launch</i>Social Area</a>
<li><a href="{{ url_for('main.social_area', _anchor='public-corproa') }}"><i class="nopaque-icons">I</i>Public Corpora</a></li> <ul>
<li>
<a class="waves-effect" href="{{ url_for('main.social_area', _anchor='public-users') }}" style="padding-left: 47px;"><i class="material-icons">person</i>Public Users</a>
</li>
<li>
<a class="waves-effect" href="{{ url_for('main.social_area', _anchor='public-corproa') }}" style="padding-left: 47px;"><i class="nopaque-icons">I</i>Public Corpora</a>
</li>
</ul>
</li>
<li class="hide-on-large-only"><div class="divider"></div></li> <li class="hide-on-large-only"><div class="divider"></div></li>
<li class="hide-on-large-only"><a class="subheader">Account</a></li> <li class="hide-on-large-only"><a class="subheader">Account</a></li>
<li class="hide-on-large-only"><a href="{{ url_for('settings.settings') }}"><i class="material-icons left">settings</i>Settings</a></li> <li class="hide-on-large-only">
<li class="hide-on-large-only"><a href="{{ url_for('auth.logout') }}">Log out</a></li> <a class="waves-effect" href="{{ url_for('settings.settings') }}"><i class="material-icons left">settings</i>Settings</a>
</li>
<li class="hide-on-large-only">
<a class="waves-effect" href="{{ url_for('auth.logout') }}">Log out</a>
</li>
{% if current_user.can('ADMINISTRATE') %} {% if current_user.can('ADMINISTRATE') %}
<li><div class="divider"></div></li> <li><div class="divider"></div></li>
<li><a class="subheader">Administration</a></li> <li>
<li><a href="{{ url_for('admin.corpora') }}"><i class="nopaque-icons">I</i>Corpora</a></li> <a class="waves-effect" href="{{ url_for('admin.admin') }}"><i class="material-icons">admin_panel_settings</i>Administration</a>
<li><a href="{{ url_for('admin.corpora') }}"><i class="nopaque-icons">J</i>Jobs</a></li> </li>
<li><a href="{{ url_for('admin.users') }}"><i class="material-icons">group</i>Users</a></li>
{% endif %} {% endif %}
{% if current_user.can('USE_API') %} {% if current_user.can('USE_API') %}
<li><div class="divider"></div></li> <li>
<li><a class="subheader">API</a></li> <a class="waves-effect" href="{{ url_for('apifairy.docs') }}"><i class="material-icons">api</i>API</a>
<li><a href="{{ url_for('apifairy.docs') }}"><i class="material-icons">api</i>API</a></li> </li>
{% endif %} {% endif %}
</ul> </ul>

View File

@ -5,7 +5,7 @@
<div class="row"> <div class="row">
<div class="col s12 l2"> <div class="col s12 l2">
<p>&nbsp;</p> <p>&nbsp;</p>
<img src="{{ url_for('users.profile_avatar', user_id=user.id) }}" alt="user-image" class="circle responsive-img"> <img src="{{ url_for('users.user_avatar', user_id=user.id) }}" alt="user-image" class="circle responsive-img">
</div> </div>
<div class="col s12 l10"> <div class="col s12 l10">
<h1 id="title">{{ title }}</h1> <h1 id="title">{{ title }}</h1>

View File

@ -48,11 +48,7 @@
<table> <table>
<tr> <tr>
<td style="width:10%; margin-top:25px;"> <td style="width:10%; margin-top:25px;">
{% if corpus.user.avatar %} <img src="{{ url_for('users.user_avatar', user_id=corpus.user.id) }}" alt="user-image" class="circle responsive-img">
<img src="{{ url_for('users.profile_avatar', user_id=corpus.user.id) }}" alt="user-image" class="circle responsive-img">
{% else %}
<img src="{{ url_for('static', filename='images/user_avatar.png') }}" alt="user-image" class="circle responsive-img">
{% endif %}
</td> </td>
<td></td> <td></td>
<td> <td>

View File

@ -114,7 +114,7 @@
<p></p> <p></p>
<div class="row"> <div class="row">
<div class="col s12 m2"> <div class="col s12 m2">
<img src="{{ url_for('users.profile_avatar', user_id=current_user.id) }}" alt="user-image" class="circle responsive-img" id="avatar"> <img src="{{ url_for('users.user_avatar', user_id=current_user.id) }}" alt="user-image" class="circle responsive-img" id="avatar">
</div> </div>
<div class="col s12 m6"> <div class="col s12 m6">
{{ wtf.render_field(edit_public_profile_information_form.avatar, accept='image/jpeg, image/png, image/gif', placeholder='Choose an image file', id='avatar-upload') }} {{ wtf.render_field(edit_public_profile_information_form.avatar, accept='image/jpeg, image/png, image/gif', placeholder='Choose an image file', id='avatar-upload') }}

View File

@ -1,6 +1,7 @@
{% extends "base.html.j2" %} {% extends "base.html.j2" %}
{% import "materialize/wtf.html.j2" as wtf %} {% import "materialize/wtf.html.j2" as wtf %}
{% block page_content %} {% block page_content %}
<div class="container"> <div class="container">
<div class="row"> <div class="row">
@ -10,9 +11,8 @@
<div class="row"> <div class="row">
<div class="col s1"></div> <div class="col s1"></div>
<div class="col s3"> <div class="col s3">
<br> <p>&nbsp;</p>
<br> <img src="{{ url_for('.user_avatar', user_id=user.id) }}" alt="user-image" class="circle responsive-img">
<img src="{{ url_for('.profile_avatar', user_id=user_id) }}" alt="user-image" class="circle responsive-img">
</div> </div>
<div class="col s1"></div> <div class="col s1"></div>
<div class="col s7"> <div class="col s7">
@ -27,16 +27,18 @@
{% else %} {% else %}
<span class="chip white-text" style="background-color: #f44336;">Private Profile</span> <span class="chip white-text" style="background-color: #f44336;">Private Profile</span>
{% endif %} {% endif %}
{% if user.has_profile_privacy_setting('SHOW_MEMBER_SINCE') %}
<span class="chip">Member since: {{ user.member_since.strftime('%Y-%m-%d') }}</span>
{% endif %}
{% if user.has_profile_privacy_setting('SHOW_LAST_SEEN') %}
<span class="chip">Last seen: {{ user.last_seen.strftime('%Y-%m-%d') }}</span>
{% endif %}
</div> </div>
<div class="col 12"> <div class="col 12">
{% if user.show_last_seen %}
<div class="chip">Last seen: {{ last_seen }}</div>
{% endif %}
{% if user.location %} {% if user.location %}
<p><span class="material-icons" style="margin-right:20px; margin-top:20px;">location_on</span><i>{{ user.location }}</i></p> <p>&nbsp;</p>
<p><i class="material-icons left">location_on</i>{{ user.location }}</p>
{% endif %} {% endif %}
<p></p>
<br>
{% if user.about_me %} {% if user.about_me %}
<blockquote> <blockquote>
<h5>About me</h5> <h5>About me</h5>
@ -59,7 +61,7 @@
<td>{{ user.full_name }} </td> <td>{{ user.full_name }} </td>
</tr> </tr>
{% endif %} {% endif %}
{% if user.show_email %} {% if user.has_profile_privacy_setting('SHOW_EMAIL') %}
<tr> <tr>
<td><span class="material-icons">email</span></td> <td><span class="material-icons">email</span></td>
<td>{{ user.email }}</td> <td>{{ user.email }}</td>
@ -78,18 +80,17 @@
</tr> </tr>
{% endif %} {% endif %}
</table> </table>
<br> <p>&nbsp;</p>
{% if user.show_member_since %}
<p><i>Member since: {{ member_since }}</i></p>
{% endif %}
<p></p>
<br>
{% if current_user.hashid == user.id %}
<a class="waves-effect waves-light btn-small" href="{{ url_for('settings.settings') }}">Edit profile</a>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
{% if current_user == user %}
<div class="card-action">
<p class="right-align">
<a class="btn waves-effect waves-light" href="{{ url_for('settings.settings') }}">Edit profile</a>
</p>
</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
@ -119,8 +120,22 @@
{{ super() }} {{ super() }}
<script> <script>
let followedCorpusList = new FollowedCorpusList(document.querySelector('.followed-corpus-list')); let followedCorpusList = new FollowedCorpusList(document.querySelector('.followed-corpus-list'));
followedCorpusList.add({{ followed_corpora|tojson }}); followedCorpusList.add(
[
{% for corpus in user.followed_corpora %}
{{ corpus.to_json_serializeable()|tojson }}
{% if not loop.last %},{% endif %}
{% endfor %}
]
);
let publicCorpusList = new PublicCorpusList(document.querySelector('.public-corpus-list')); let publicCorpusList = new PublicCorpusList(document.querySelector('.public-corpus-list'));
publicCorpusList.add({{ own_public_corpora|tojson }}); publicCorpusList.add(
[
{% for corpus in user.corpora if corpus.is_public %}
{{ corpus.to_json_serializeable()|tojson }}
{% if not loop.last %},{% endif %}
{% endfor %}
]
);
</script> </script>
{% endblock scripts %} {% endblock scripts %}

View File

@ -25,32 +25,18 @@ def users():
@login_required @login_required
def user(user_id): def user(user_id):
user = User.query.get_or_404(user_id) user = User.query.get_or_404(user_id)
last_seen = user.last_seen.strftime('%Y-%m-%d %H:%M') if not (user.is_public or user == current_user or current_user.is_administrator()):
member_since = user.member_since.strftime('%Y-%m-%d')
followed_corpora = [
c.to_json_serializeable() for c in user.followed_corpora
]
own_public_corpora = [
c.to_json_serializeable() for c
in Corpus.query.filter_by(is_public = True, user = user).all()
]
if not user.is_public and user != current_user:
abort(403) abort(403)
return render_template( return render_template(
'users/profile.html.j2', 'users/user.html.j2',
followed_corpora=followed_corpora,
last_seen=last_seen,
member_since=member_since,
own_public_corpora=own_public_corpora,
user=user, user=user,
user_id=user_id,
title=user.username title=user.username
) )
@bp.route('/<hashid:user_id>/avatar') @bp.route('/<hashid:user_id>/avatar')
@login_required @login_required
def profile_avatar(user_id): def user_avatar(user_id):
user = User.query.get_or_404(user_id) user = User.query.get_or_404(user_id)
if not (user.is_public or user == current_user or current_user.is_administrator()): if not (user.is_public or user == current_user or current_user.is_administrator()):
abort(403) abort(403)