mirror of
				https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
				synced 2025-11-03 20:02:47 +00:00 
			
		
		
		
	Merge branch 'public-corpus' of gitlab.ub.uni-bielefeld.de:sfb1288inf/nopaque into public-corpus
This commit is contained in:
		@@ -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):
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,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,
 | 
			
		||||
@@ -34,12 +34,6 @@ from .forms import (
 | 
			
		||||
    UpdateCorpusFileForm
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/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():
 | 
			
		||||
@@ -52,7 +46,7 @@ def fake_add():
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/is_public', methods=['POST'])
 | 
			
		||||
@login_required
 | 
			
		||||
@owner_or_admin_required()
 | 
			
		||||
@corpus_owner_or_admin_required()
 | 
			
		||||
def update_corpus_is_public(corpus_id):
 | 
			
		||||
    is_public = request.json
 | 
			
		||||
    if not isinstance(is_public, bool):
 | 
			
		||||
@@ -67,7 +61,7 @@ def update_corpus_is_public(corpus_id):
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/followers/add', methods=['POST'])
 | 
			
		||||
@login_required
 | 
			
		||||
@owner_or_admin_required()
 | 
			
		||||
@corpus_owner_or_admin_required()
 | 
			
		||||
def add_corpus_followers(corpus_id):
 | 
			
		||||
    usernames = request.json
 | 
			
		||||
    if not (isinstance(usernames, list) or all(isinstance(u, str) for u in usernames)):
 | 
			
		||||
@@ -124,7 +118,7 @@ def current_user_unfollow_corpus(corpus_id):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/followers/<hashid:follower_id>/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()):
 | 
			
		||||
@@ -218,6 +212,7 @@ def generate_corpus_share_link(corpus_id):
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>', methods=['DELETE'])
 | 
			
		||||
@login_required
 | 
			
		||||
@corpus_owner_or_admin_required()
 | 
			
		||||
def delete_corpus(corpus_id):
 | 
			
		||||
    def _delete_corpus(app, corpus_id):
 | 
			
		||||
        with app.app_context():
 | 
			
		||||
@@ -226,8 +221,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)
 | 
			
		||||
@@ -238,12 +231,9 @@ def delete_corpus(corpus_id):
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/analyse')
 | 
			
		||||
@login_required
 | 
			
		||||
@corpus_follower_permission_required('VIEW')
 | 
			
		||||
def analyse_corpus(corpus_id):
 | 
			
		||||
    corpus = Corpus.query.get_or_404(corpus_id)
 | 
			
		||||
    if not (corpus.user == current_user
 | 
			
		||||
            or current_user.is_administrator()
 | 
			
		||||
            or current_user.is_following_corpus(corpus)):
 | 
			
		||||
        abort(403)
 | 
			
		||||
    return render_template(
 | 
			
		||||
        'corpora/analyse_corpus.html.j2',
 | 
			
		||||
        corpus=corpus,
 | 
			
		||||
@@ -253,6 +243,7 @@ def analyse_corpus(corpus_id):
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/build', methods=['POST'])
 | 
			
		||||
@login_required
 | 
			
		||||
@corpus_owner_or_admin_required()
 | 
			
		||||
def build_corpus(corpus_id):
 | 
			
		||||
    def _build_corpus(app, corpus_id):
 | 
			
		||||
        with app.app_context():
 | 
			
		||||
@@ -277,6 +268,7 @@ def build_corpus(corpus_id):
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/files/create', methods=['GET', 'POST'])
 | 
			
		||||
@login_required
 | 
			
		||||
@corpus_follower_permission_required('ADD_CORPUS_FILE')
 | 
			
		||||
def create_corpus_file(corpus_id):
 | 
			
		||||
    corpus = Corpus.query.get_or_404(corpus_id)
 | 
			
		||||
    if not (corpus.user == current_user or current_user.is_administrator()):
 | 
			
		||||
@@ -324,10 +316,9 @@ def create_corpus_file(corpus_id):
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/files/<hashid:corpus_file_id>', methods=['GET', 'POST'])
 | 
			
		||||
@login_required
 | 
			
		||||
@corpus_follower_permission_required('ADD_CORPUS_FILE', 'UPDATE_CORPUS_FILE', 'REMOVE_CORPUS_FILE')
 | 
			
		||||
def corpus_file(corpus_id, corpus_file_id):
 | 
			
		||||
    corpus_file = CorpusFile.query.filter_by(corpus_id = corpus_id, id=corpus_file_id).first_or_404()
 | 
			
		||||
    if not (corpus_file.corpus.user == current_user or current_user.is_administrator()):
 | 
			
		||||
        abort(403)
 | 
			
		||||
    form = UpdateCorpusFileForm(data=corpus_file.to_json_serializeable())
 | 
			
		||||
    if form.validate_on_submit():
 | 
			
		||||
        form.populate_obj(corpus_file)
 | 
			
		||||
@@ -348,6 +339,7 @@ def corpus_file(corpus_id, corpus_file_id):
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/files/<hashid:corpus_file_id>', methods=['DELETE'])
 | 
			
		||||
@login_required
 | 
			
		||||
@corpus_follower_permission_required('REMOVE_CORPUS_FILE')
 | 
			
		||||
def delete_corpus_file(corpus_id, corpus_file_id):
 | 
			
		||||
    def _delete_corpus_file(app, corpus_file_id):
 | 
			
		||||
        with app.app_context():
 | 
			
		||||
@@ -368,6 +360,7 @@ def delete_corpus_file(corpus_id, corpus_file_id):
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/files/<hashid:corpus_file_id>/download')
 | 
			
		||||
@login_required
 | 
			
		||||
@corpus_follower_permission_required('VIEW')
 | 
			
		||||
def download_corpus_file(corpus_id, corpus_file_id):
 | 
			
		||||
    corpus_file = CorpusFile.query.filter_by(corpus_id = corpus_id, id=corpus_file_id).first_or_404()
 | 
			
		||||
    if not (corpus_file.corpus.user == current_user or current_user.is_administrator()):
 | 
			
		||||
 
 | 
			
		||||
@@ -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'
 | 
			
		||||
    )
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								app/static/js/ResourceLists/FollowedCorpusList.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/static/js/ResourceLists/FollowedCorpusList.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
class FollowedCorpusList extends CorpusList {
 | 
			
		||||
  get item() {
 | 
			
		||||
    return `
 | 
			
		||||
      <tr class="list-item clickable hoverable">
 | 
			
		||||
        <td><a class="btn-floating disabled"><i class="material-icons service-color darken" data-service="corpus-analysis">book</i></a></td>
 | 
			
		||||
        <td><b class="title"></b><br><i class="description"></i></td>
 | 
			
		||||
        <td><span class="status badge new corpus-status-color corpus-status-text" data-badge-caption=""></span></td>
 | 
			
		||||
        <td class="right-align">
 | 
			
		||||
          <a class="list-action-trigger btn-floating service-color darken waves-effect waves-light" data-list-action="view" data-service="corpus-analysis"><i class="material-icons">send</i></a>
 | 
			
		||||
        </td>
 | 
			
		||||
      </tr>
 | 
			
		||||
    `.trim();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -13,14 +13,14 @@ class UserList extends ResourceList {
 | 
			
		||||
  get item() {
 | 
			
		||||
    return `
 | 
			
		||||
      <tr class="list-item clickable hoverable">
 | 
			
		||||
        <td><img alt="user-image" class="circle responsive-img avatar" style="width:50%"></td>
 | 
			
		||||
        <td><img alt="user-image" class="circle responsive-img avatar" style="width:25%"></td>
 | 
			
		||||
        <td><b><span class="username"></span><b></td>
 | 
			
		||||
        <td><span class="full-name"></span></td>
 | 
			
		||||
        <td><span class="location"></span></td>
 | 
			
		||||
        <td><span class="organization"></span></td>
 | 
			
		||||
        <td><span class="corpora-online"></span></td>
 | 
			
		||||
        <td class="right-align">
 | 
			
		||||
          <a class="list-action-trigger btn-floating waves-effect waves-light" data-list-action="view"><i class="material-icons">send</i></a>
 | 
			
		||||
          <a class="list-action-trigger btn-floating waves-effect waves-light" data-list-action="view" style="background-color:#D9A36D"><i class="material-icons">send</i></a>
 | 
			
		||||
        </td>
 | 
			
		||||
      </tr>
 | 
			
		||||
    `.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
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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',
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,10 @@
 | 
			
		||||
        <div class="col s4">
 | 
			
		||||
            <a href="{{ url_for('users.user', user_id=current_user.id) }}">
 | 
			
		||||
            {% if current_user.avatar %}
 | 
			
		||||
            <img src="{{ url_for('users.profile_avatar', user_id=current_user.id) }}" alt="user-image" class="circle responsive-img"  style="height:80%; margin-top: 20px; 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;">
 | 
			
		||||
            {% else %}
 | 
			
		||||
            <img src="{{ url_for('static', filename='images/user_avatar.png') }}" alt="user-image" class="circle responsive-img" style="height:80%; margin-top: 20px; margin-left:-15px;">
 | 
			
		||||
            <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 %}
 | 
			
		||||
            {# <i class="material-icons" style="color:white; font-size:3em; margin-top: 25px; margin-left:-15px;">account_circle</i></div> #}
 | 
			
		||||
            </a>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="col s8">
 | 
			
		||||
@@ -27,6 +26,7 @@
 | 
			
		||||
  <li><a href="{{ url_for('main.dashboard', _anchor='corpora') }}" style="padding-left: 47px;"><i class="nopaque-icons">I</i>My Corpora</a></li>
 | 
			
		||||
  <li><a href="{{ url_for('main.dashboard', _anchor='jobs') }}" style="padding-left: 47px;"><i class="nopaque-icons">J</i>My Jobs</a></li>
 | 
			
		||||
  <li><a href="{{ url_for('contributions.contributions') }}"><i class="material-icons">new_label</i>Contribute</a></li>
 | 
			
		||||
  <li><a href="{{ url_for('main.social_area') }}"><i class="material-icons">group</i>Social Area</a></li>
 | 
			
		||||
  <li><div class="divider"></div></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>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										70
									
								
								app/templates/main/social_area.html.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								app/templates/main/social_area.html.j2
									
									
									
									
									
										Normal file
									
								
							@@ -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 %}
 | 
			
		||||
<div class="container">
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col s12">
 | 
			
		||||
      <h1 id="title">{{ title }}</h1>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="col s12 m3 push-m9">
 | 
			
		||||
      <div class="center-align">
 | 
			
		||||
        <p class="hide-on-small-only"> </p>
 | 
			
		||||
        <p class="hide-on-small-only"> </p>
 | 
			
		||||
        <a class="btn-floating btn-large btn-scale-x2 waves-effect waves-light" style="background-color:#D9A36D">
 | 
			
		||||
          <i class="left material-icons">group</i>
 | 
			
		||||
        </a>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="col s12 m9 pull-m3">
 | 
			
		||||
      <div class="card" style="border-top: 10px solid #D9A36D;">
 | 
			
		||||
        <div class="card-content">
 | 
			
		||||
          <div class="row">
 | 
			
		||||
            <div class="col s12">
 | 
			
		||||
              <div class="card-panel z-depth-0">
 | 
			
		||||
                <span class="card-title"><i class="left material-icons">layers</i>Your social area</span>
 | 
			
		||||
                <p>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.</p>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="col s12">
 | 
			
		||||
      <h3>Other Users</h3>
 | 
			
		||||
      <p>Find other users and see what corpora they have made public.</p>
 | 
			
		||||
      <div class="card">
 | 
			
		||||
        <div class="card-content">
 | 
			
		||||
          <div class="user-list no-autoinit"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="col s12">
 | 
			
		||||
      <h3>Public Corpora</h3>
 | 
			
		||||
      <p>Find public corpora.</p>
 | 
			
		||||
      <div class="card">
 | 
			
		||||
        <div class="card-content">
 | 
			
		||||
          <span class="card-title">Public Corpora</span>
 | 
			
		||||
          <div class="public-corpus-list no-autoinit"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
{% endblock page_content %}
 | 
			
		||||
 | 
			
		||||
{% block scripts %}
 | 
			
		||||
{{ super() }}
 | 
			
		||||
<script>
 | 
			
		||||
  let userList = new UserList(document.querySelector('.user-list'));
 | 
			
		||||
  userList.add({{ users|tojson }});
 | 
			
		||||
  let publicCorpusList = new PublicCorpusList(document.querySelector('.public-corpus-list'));
 | 
			
		||||
  publicCorpusList.add({{ corpora|tojson }});
 | 
			
		||||
</script>
 | 
			
		||||
{% endblock scripts %}
 | 
			
		||||
@@ -26,7 +26,7 @@
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div class="col 12">
 | 
			
		||||
                    {% if user.show_last_seen %}
 | 
			
		||||
                    <div class="chip">Last seen: {{ user.last_seen }}</div>
 | 
			
		||||
                    <div class="chip">Last seen: {{ last_seen }}</div>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                    {% if user.location %}
 | 
			
		||||
                    <p><span class="material-icons" style="margin-right:20px; margin-top:20px;">location_on</span><i>{{ user.location }}</i></p>
 | 
			
		||||
@@ -76,7 +76,7 @@
 | 
			
		||||
                </table>
 | 
			
		||||
                <br>
 | 
			
		||||
                {% if user.show_member_since %}
 | 
			
		||||
                <p><i>Member since: {{ user.member_since }}</i></p>
 | 
			
		||||
                <p><i>Member since: {{ member_since }}</i></p>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                <p></p>
 | 
			
		||||
                <br>
 | 
			
		||||
@@ -93,7 +93,8 @@
 | 
			
		||||
      <div class="col s6">
 | 
			
		||||
        <div class="card">
 | 
			
		||||
          <div class="card-content">
 | 
			
		||||
            <h4>Groups</h4>
 | 
			
		||||
            <h4>Followed corpora</h4>
 | 
			
		||||
            <div class="followed-corpus-list no-autoinit"></div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
@@ -101,7 +102,7 @@
 | 
			
		||||
        <div class="card">
 | 
			
		||||
          <div class="card-content">
 | 
			
		||||
            <h4>Public corpora</h4>
 | 
			
		||||
              <div class="public-corpora-list" data-user-id="{{ user.hashid }}"></div>
 | 
			
		||||
            <div class="public-corpus-list no-autoinit"></div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
@@ -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 }});
 | 
			
		||||
</script>
 | 
			
		||||
{% endblock scripts %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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('/<hashid:user_id>')
 | 
			
		||||
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('/<hashid:user_id>/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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user