From b1586b3679fd8c1c3089739433eb302de3218fac Mon Sep 17 00:00:00 2001 From: Inga Kirschnick Date: Wed, 1 Mar 2023 14:09:15 +0100 Subject: [PATCH] social-area page and profile page update --- app/corpora/decorators.py | 11 ++- app/corpora/routes.py | 17 ++--- app/main/routes.py | 32 +++++---- .../js/ResourceLists/FollowedCorpusList.js | 14 ++++ app/static/js/ResourceLists/UserList.js | 6 +- app/templates/_scripts.html.j2 | 1 + app/templates/_sidenav.html.j2 | 6 +- app/templates/main/social_area.html.j2 | 70 +++++++++++++++++++ app/templates/users/profile.html.j2 | 14 ++-- app/users/routes.py | 31 ++++---- 10 files changed, 144 insertions(+), 58 deletions(-) create mode 100644 app/static/js/ResourceLists/FollowedCorpusList.js create mode 100644 app/templates/main/social_area.html.j2 diff --git a/app/corpora/decorators.py b/app/corpora/decorators.py index 3d841e9b..b3499258 100644 --- a/app/corpora/decorators.py +++ b/app/corpora/decorators.py @@ -3,27 +3,24 @@ from flask_login import current_user from functools import wraps from app.models import Corpus, CorpusFollowerAssociation -def corpus_follower_permission_required(permissions): +def corpus_follower_permission_required(*permissions): def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): corpus_id = kwargs.get('corpus_id') corpus = Corpus.query.get_or_404(corpus_id) if current_user == corpus.user or current_user.is_administrator(): - print('user or admin') return f(*args, **kwargs) if not current_user.is_following_corpus(corpus): - print('not following corpus') abort(403) corpus_follower_association = CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id, follower_id=current_user.id).first_or_404() - for permission in permissions: - if not corpus_follower_association.role.has_permission(permission): - abort(403) + if not all([corpus_follower_association.role.has_permission(p) for p in permissions]): + abort(403) return f(*args, **kwargs) return decorated_function return decorator -def owner_or_admin_required(): +def corpus_owner_or_admin_required(): def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): diff --git a/app/corpora/routes.py b/app/corpora/routes.py index 9a69ebd8..c2af713a 100644 --- a/app/corpora/routes.py +++ b/app/corpora/routes.py @@ -15,7 +15,7 @@ from flask_login import current_user, login_required from threading import Thread import jwt import os -from .decorators import corpus_follower_permission_required, owner_or_admin_required +from .decorators import corpus_follower_permission_required, corpus_owner_or_admin_required from app import db, hashids from app.models import ( Corpus, @@ -33,12 +33,6 @@ from .forms import ( UpdateCorpusFileForm ) -@bp.route('//test') -@login_required -@corpus_follower_permission_required(['VIEW', 'ADD_CORPUS_FILE']) -def test(corpus_id): - return 'ok' - @bp.route('/fake-add') @login_required def fake_add(): @@ -51,7 +45,7 @@ def fake_add(): @bp.route('//is_public/enable', methods=['POST']) @login_required -@owner_or_admin_required() +@corpus_owner_or_admin_required() def enable_corpus_is_public(corpus_id): corpus = Corpus.query.get_or_404(corpus_id) corpus.is_public = True @@ -61,7 +55,7 @@ def enable_corpus_is_public(corpus_id): @bp.route('//is_public/disable', methods=['POST']) @login_required -@owner_or_admin_required() +@corpus_owner_or_admin_required() def disable_corpus_is_public(corpus_id): corpus = Corpus.query.get_or_404(corpus_id) corpus.is_public = False @@ -111,7 +105,7 @@ def current_user_unfollow_corpus(corpus_id): @bp.route('//followers//role', methods=['POST']) -@corpus_follower_permission_required(['REMOVE_FOLLOWER', 'UPDATE_FOLLOWER']) +@corpus_follower_permission_required('REMOVE_FOLLOWER', 'UPDATE_FOLLOWER') def add_permission(corpus_id, follower_id): corpus_follower_association = CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id, follower_id=follower_id).first_or_404() if not (corpus_follower_association.corpus.user == current_user or current_user.is_administrator()): @@ -206,6 +200,7 @@ def generate_corpus_share_link(corpus_id): @bp.route('/', methods=['DELETE']) @login_required +@corpus_owner_or_admin_required() def delete_corpus(corpus_id): def _delete_corpus(app, corpus_id): with app.app_context(): @@ -214,8 +209,6 @@ def delete_corpus(corpus_id): db.session.commit() corpus = Corpus.query.get_or_404(corpus_id) - if not (corpus.user == current_user or current_user.is_administrator()): - abort(403) thread = Thread( target=_delete_corpus, args=(current_app._get_current_object(), corpus_id) diff --git a/app/main/routes.py b/app/main/routes.py index eff0dadd..287a04f5 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -27,20 +27,7 @@ def faq(): @bp.route('/dashboard') @login_required def dashboard(): - # users = [ - # u.to_json_serializeable(filter_by_privacy_settings=True) for u - # in User.query.filter(User.is_public == True, User.id != current_user.id).all() - # ] - # corpora = [ - # c.to_json_serializeable() for c - # in Corpus.query.filter(Corpus.is_public == True, Corpus.user != current_user).all() - # ] - return render_template( - 'main/dashboard.html.j2', - title='Dashboard', - # users=users, - # corpora=corpora - ) + return render_template('main/dashboard.html.j2', title='Dashboard') @bp.route('/dashboard2') @@ -67,3 +54,20 @@ def privacy_policy(): @bp.route('/terms_of_use') def terms_of_use(): return render_template('main/terms_of_use.html.j2', title='Terms of Use') + +@bp.route('/social-area') +def social_area(): + users = [ + u.to_json_serializeable(relationships=True, filter_by_privacy_settings=True,) for u + in User.query.filter(User.is_public == True, User.id != current_user.id).all() + ] + corpora = [ + c.to_json_serializeable() for c + in Corpus.query.filter(Corpus.is_public == True, Corpus.user != current_user).all() + ] + return render_template( + 'main/social_area.html.j2', + users=users, + corpora=corpora, + title='Social Area' + ) diff --git a/app/static/js/ResourceLists/FollowedCorpusList.js b/app/static/js/ResourceLists/FollowedCorpusList.js new file mode 100644 index 00000000..b48bfd76 --- /dev/null +++ b/app/static/js/ResourceLists/FollowedCorpusList.js @@ -0,0 +1,14 @@ +class FollowedCorpusList extends CorpusList { + get item() { + return ` + + book +
+ + + send + + + `.trim(); + } +} diff --git a/app/static/js/ResourceLists/UserList.js b/app/static/js/ResourceLists/UserList.js index 03fabd73..65542de0 100644 --- a/app/static/js/ResourceLists/UserList.js +++ b/app/static/js/ResourceLists/UserList.js @@ -13,14 +13,14 @@ class UserList extends ResourceList { get item() { return ` - user-image + user-image - send + send `.trim(); @@ -77,7 +77,7 @@ class UserList extends ResourceList { 'full-name': user.full_name ? user.full_name : '', 'location': user.location ? user.location : '', 'organization': user.organization ? user.organization : '', - 'corpora-online': '-' + 'corpora-online': Object.values(user.corpora).filter((corpus) => corpus.is_public).length }; }; diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2 index 2a3626ab..18cde5b8 100644 --- a/app/templates/_scripts.html.j2 +++ b/app/templates/_scripts.html.j2 @@ -22,6 +22,7 @@ 'js/ResourceLists/CorpusFileList.js', 'js/ResourceLists/PublicCorpusFileList.js', 'js/ResourceLists/CorpusList.js', + 'js/ResourceLists/FollowedCorpusList.js', 'js/ResourceLists/PublicCorpusList.js', 'js/ResourceLists/JobList.js', 'js/ResourceLists/JobInputList.js', diff --git a/app/templates/_sidenav.html.j2 b/app/templates/_sidenav.html.j2 index 18b902af..62dcc604 100644 --- a/app/templates/_sidenav.html.j2 +++ b/app/templates/_sidenav.html.j2 @@ -6,11 +6,10 @@ #}
@@ -27,6 +26,7 @@
  • IMy Corpora
  • JMy Jobs
  • new_labelContribute
  • +
  • groupSocial Area
  • Processes & Services
  • File setup
  • diff --git a/app/templates/main/social_area.html.j2 b/app/templates/main/social_area.html.j2 new file mode 100644 index 00000000..fbe6974e --- /dev/null +++ b/app/templates/main/social_area.html.j2 @@ -0,0 +1,70 @@ +{% extends "base.html.j2" %} +{% import "materialize/wtf.html.j2" as wtf %} + +{% block main_attribs %} style="background-color:#d8c9ba86" {% endblock main_attribs %} + +{% block page_content %} +
    +
    +
    +

    {{ title }}

    +
    + +
    +
    +

     

    +

     

    + + group + +
    +
    + +
    +
    +
    +
    +
    +
    + layersYour social area +

    Here you can network with your team and other users. You can find corpora that are public and request them or just see what other users are working on.

    +
    +
    +
    +
    +
    +
    + +
    +

    Other Users

    +

    Find other users and see what corpora they have made public.

    +
    +
    +
    +
    +
    +
    + +
    +

    Public Corpora

    +

    Find public corpora.

    +
    +
    + Public Corpora +
    +
    +
    +
    +
    +
    +{% endblock page_content %} + +{% block scripts %} +{{ super() }} + +{% endblock scripts %} diff --git a/app/templates/users/profile.html.j2 b/app/templates/users/profile.html.j2 index a6ad5b30..4a9a4c13 100644 --- a/app/templates/users/profile.html.j2 +++ b/app/templates/users/profile.html.j2 @@ -26,7 +26,7 @@
    {% if user.show_last_seen %} -
    Last seen: {{ user.last_seen }}
    +
    Last seen: {{ last_seen }}
    {% endif %} {% if user.location %}

    location_on{{ user.location }}

    @@ -76,7 +76,7 @@
    {% if user.show_member_since %} -

    Member since: {{ user.member_since }}

    +

    Member since: {{ member_since }}

    {% endif %}


    @@ -93,7 +93,8 @@
    -

    Groups

    +

    Followed corpora

    +
    @@ -101,7 +102,7 @@

    Public corpora

    -
    +
    @@ -127,6 +128,11 @@ if ("{{ user.id }}" == "{{ current_user.hashid }}") { } else { publicInformationBadge.remove(); } + +let followedCorpusList = new FollowedCorpusList(document.querySelector('.followed-corpus-list')); +followedCorpusList.add({{ followed_corpora|tojson }}); +let publicCorpusList = new PublicCorpusList(document.querySelector('.public-corpus-list')); +publicCorpusList.add({{ own_public_corpora|tojson }}); {% endblock scripts %} diff --git a/app/users/routes.py b/app/users/routes.py index 48a8d7b5..d573da34 100644 --- a/app/users/routes.py +++ b/app/users/routes.py @@ -1,3 +1,4 @@ +from datetime import datetime from flask import ( abort, current_app, @@ -12,7 +13,7 @@ from flask_login import current_user, login_required from threading import Thread import os from app import db -from app.models import Avatar, ProfilePrivacySettings, User +from app.models import Avatar, Corpus, ProfilePrivacySettings, User from . import bp from .forms import ( EditPrivacySettingsForm, @@ -29,10 +30,23 @@ def before_request(): @login_required def user(user_id): user = User.query.get_or_404(user_id) + last_seen = user.last_seen.strftime('%Y-%m-%d %H:%M') + 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) return render_template( - 'users/profile.html.j2', + 'users/profile.html.j2', + followed_corpora=followed_corpora, + last_seen=last_seen, + member_since=member_since, + own_public_corpora=own_public_corpora, user=user.to_json_serializeable(), user_id=user_id ) @@ -56,18 +70,6 @@ def delete_user(user_id): thread.start() return {}, 202 -@bp.route('/') -def profile(user_id): - user = User.query.get_or_404(user_id) - if not user.is_public and user != current_user: - abort(403) - return render_template( - 'users/profile.html.j2', - user=user.to_json_serializeable(), - user_id=user_id - ) - - @bp.route('//avatar') def profile_avatar(user_id): user = User.query.get_or_404(user_id) @@ -91,7 +93,6 @@ def delete_profile_avatar(user_id): avatar = Avatar.query.get(avatar_id) avatar.delete() db.session.commit() - user = User.query.get_or_404(user_id) if user.avatar is None: abort(404)