diff --git a/app/corpora/forms.py b/app/corpora/forms.py index 12ec1d9c..8950622d 100644 --- a/app/corpora/forms.py +++ b/app/corpora/forms.py @@ -1,6 +1,7 @@ from flask_wtf import FlaskForm from flask_wtf.file import FileField, FileRequired from wtforms import ( + BooleanField, StringField, SubmitField, TextAreaField, @@ -77,6 +78,9 @@ class UpdateCorpusFileForm(CorpusFileBaseForm): kwargs['prefix'] = 'update-corpus-file-form' super().__init__(*args, **kwargs) +class ChangeCorpusSettingsForm(FlaskForm): + is_public = BooleanField('Public Corpus') + submit = SubmitField() class ImportCorpusForm(FlaskForm): pass diff --git a/app/corpora/routes.py b/app/corpora/routes.py index f3cdf8f1..a5035087 100644 --- a/app/corpora/routes.py +++ b/app/corpora/routes.py @@ -5,15 +5,17 @@ from flask import ( Markup, redirect, render_template, - send_from_directory + request, + send_from_directory, + url_for ) from flask_login import current_user, login_required from threading import Thread import os -from app import db -from app.models import Corpus, CorpusFile, CorpusStatus +from app import db, hashids +from app.models import Corpus, CorpusFile, CorpusStatus, CorpusFollowerAssociation, User from . import bp -from .forms import CreateCorpusFileForm, CreateCorpusForm, UpdateCorpusFileForm +from .forms import ChangeCorpusSettingsForm, CreateCorpusFileForm, CreateCorpusForm, UpdateCorpusFileForm def user_can_read_corpus(user, corpus): @@ -64,19 +66,30 @@ def create_corpus(): ) -@bp.route('/') +@bp.route('/', methods=['GET', 'POST']) @login_required def corpus(corpus_id): corpus = Corpus.query.get_or_404(corpus_id) if not user_can_read_corpus(current_user, corpus): abort(403) + corpus_settings_form = ChangeCorpusSettingsForm( + data=corpus.to_json_serializeable(), + prefix='corpus-settings-form' + ) + if corpus_settings_form.validate_on_submit(): + corpus.is_public = corpus_settings_form.is_public.data + db.session.commit() + flash('Your changes have been saved') + return redirect(url_for('.corpus', corpus_id=corpus.id)) return render_template( 'corpora/corpus.html.j2', + corpus_settings_form=corpus_settings_form, corpus=corpus, title='Corpus' ) + # @bp.route('//update') # @login_required # def update_corpus(corpus_id): @@ -263,3 +276,56 @@ def import_corpus(): @login_required def export_corpus(corpus_id): abort(503) + +@bp.route('//follow') +@login_required +# TODO: Wenn Query Paramter genutzt wird, prüfen, ob user_id ungleich current_user.id ist und dann gucken, ob es ein Admin ist. +# Sonst 403. +def follow_corpus(corpus_id): + corpus = Corpus.query.get_or_404(corpus_id) + user_hashid = request.args.get('user_id') + if user_hashid is None: + user = current_user + else: + if not current_user.is_administrator(): + abort(403) + else: + user_id = hashids.decode(user_hashid) + user = User.query.get_or_404(user_id) + if not user.is_following_corpus(corpus): + user.follow_corpus(corpus) + db.session.commit() + return {}, 202 + +@bp.route('//unfollow') +@login_required +def unfollow_corpus(corpus_id): + corpus = Corpus.query.get_or_404(corpus_id) + user_hashid = request.args.get('user_id') + if user_hashid is None: + user = current_user + else: + if not current_user.is_administrator(): + abort(403) + else: + user_id = hashids.decode(user_hashid) + user = User.query.get_or_404(user_id) + if user.is_following_corpus(corpus): + user.unfollow_corpus(corpus) + db.session.commit() + return {}, 202 + +@bp.route('/add_permission///') +def add_permission(corpus_id, user_id, permission): + a = CorpusFollowerAssociation.query.filter_by(followed_corpus_id=corpus_id, following_user_id=user_id).first_or_404() + a.add_permission(permission) + db.session.commit() + return 'ok' + + +@bp.route('/remove_permission///') +def remove_permission(corpus_id, user_id, permission): + a = CorpusFollowerAssociation.query.filter_by(followed_corpus_id=corpus_id, following_user_id=user_id).first_or_404() + a.remove_permission(permission) + db.session.commit() + return 'ok' diff --git a/app/main/routes.py b/app/main/routes.py index 9935c479..aed63853 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -1,7 +1,7 @@ from flask import flash, redirect, render_template, url_for from flask_login import current_user, login_required, login_user from app.auth.forms import LoginForm -from app.models import User +from app.models import Corpus, User from . import bp @@ -31,7 +31,11 @@ def dashboard(): 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() ] - return render_template('main/dashboard.html.j2', title='Dashboard', users=users) + corpora = [ + c.to_json_serializeable() for c + in Corpus.query.filter(Corpus.is_public == True).all() + ] + return render_template('main/dashboard.html.j2', title='Dashboard', users=users, corpora=corpora) @bp.route('/dashboard2') diff --git a/app/models.py b/app/models.py index af3d1cfc..24f59d60 100644 --- a/app/models.py +++ b/app/models.py @@ -68,6 +68,11 @@ class ProfilePrivacySettings(IntEnum): SHOW_EMAIL = 1 SHOW_LAST_SEEN = 2 SHOW_MEMBER_SINCE = 4 + +class CorpusFollowPermission(IntEnum): + VIEW = 1 + CONTRIBUTE = 2 + ADMINISTRATE = 4 # endregion enums @@ -298,6 +303,16 @@ class CorpusFollowerAssociation(db.Model): def __repr__(self): return f'' + def has_permission(self, permission): + return self.permissions & permission == permission + + def add_permission(self, permission): + if not self.has_permission(permission): + self.permissions += permission + + def remove_permission(self, permission): + if self.has_permission(permission): + self.permissions -= permission class User(HashidMixin, UserMixin, db.Model): __tablename__ = 'users' @@ -576,6 +591,18 @@ class User(HashidMixin, UserMixin, db.Model): self.profile_privacy_settings = 0 #endregion Profile Privacy settings + def follow_corpus(self, corpus): + if not self.is_following_corpus(corpus): + self.followed_corpora.append(corpus) + + def unfollow_corpus(self, corpus): + if self.is_following_corpus(corpus): + self.followed_corpora.remove(corpus) + + def is_following_corpus(self, corpus): + return corpus in self.followed_corpora + + def to_json_serializeable(self, backrefs=False, relationships=False, filter_by_privacy_settings=False): json_serializeable = { 'id': self.hashid, diff --git a/app/static/js/ResourceLists/UserList.js b/app/static/js/ResourceLists/UserList.js index d871ec31..03fabd73 100644 --- a/app/static/js/ResourceLists/UserList.js +++ b/app/static/js/ResourceLists/UserList.js @@ -1,7 +1,7 @@ class UserList extends ResourceList { static autoInit() { - for (let publicUserListElement of document.querySelectorAll('.user-list:not(.no-autoinit)')) { - new UserList(publicUserListElement); + for (let userListElement of document.querySelectorAll('.user-list:not(.no-autoinit)')) { + new UserList(userListElement); } } @@ -41,14 +41,14 @@ class UserList extends ResourceList { initListContainerElement() { if (!this.listContainerElement.hasAttribute('id')) { - this.listContainerElement.id = Utils.generateElementId('public-user-list-'); + this.listContainerElement.id = Utils.generateElementId('user-list-'); } let listSearchElementId = Utils.generateElementId(`${this.listContainerElement.id}-search-`); this.listContainerElement.innerHTML = `
search - +
@@ -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': '0' + 'corpora-online': '-' }; }; diff --git a/app/templates/_sidenav.html.j2 b/app/templates/_sidenav.html.j2 index eb547454..676b7069 100644 --- a/app/templates/_sidenav.html.j2 +++ b/app/templates/_sidenav.html.j2 @@ -20,6 +20,7 @@
  • dashboardDashboard
  • IMy Corpora
  • JMy Jobs
  • +
  • groupsSocial
  • new_labelContribute
  • Processes & Services
  • diff --git a/app/templates/corpora/corpus.html.j2 b/app/templates/corpora/corpus.html.j2 index 16af7d31..00e10bba 100644 --- a/app/templates/corpora/corpus.html.j2 +++ b/app/templates/corpora/corpus.html.j2 @@ -1,4 +1,5 @@ {% extends "base.html.j2" %} +{% import "materialize/wtf.html.j2" as wtf %} {% from "corpora/_breadcrumbs.html.j2" import breadcrumbs with context %} {% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %} @@ -10,6 +11,9 @@

    + {% if not corpus.user == current_user %} + addFollow Corpus + {% endif %}

     

    @@ -76,6 +80,24 @@
    + {% if current_user.can(Permission.ADMINISTRATE) or current_user.hashid == corpus.user.hashid %} +
    +
    + {{ corpus_settings_form.hidden_tag() }} +
    +
    + Corpus settings +
    +

    + {{ wtf.render_field(corpus_settings_form.is_public) }} +
    +
    + {{ wtf.render_field(corpus_settings_form.submit, material_icon='send') }} +
    +
    + +
    + {% endif %} {% endblock page_content %} @@ -84,5 +106,25 @@ {{ super() }} {% endblock scripts %} diff --git a/app/templates/main/dashboard.html.j2 b/app/templates/main/dashboard.html.j2 index 5474db97..5391a8af 100644 --- a/app/templates/main/dashboard.html.j2 +++ b/app/templates/main/dashboard.html.j2 @@ -42,6 +42,23 @@ +
    +

    Social

    +
    +
    + Other users +

    Find other users and follow them to see their corpora.

    +
    +
    +
    +
    +
    + Public corpora +

    Find public corpora

    +
    +
    +
    +
    {% endblock page_content %} @@ -96,3 +113,14 @@ {% endblock modals %} + +{% block scripts %} +{{ super() }} + +{% endblock scripts %} + diff --git a/app/templates/users/profile.html.j2 b/app/templates/users/profile.html.j2 index a98a372d..a6ad5b30 100644 --- a/app/templates/users/profile.html.j2 +++ b/app/templates/users/profile.html.j2 @@ -89,6 +89,24 @@ +
    +
    +
    +
    +

    Groups

    +
    +
    +
    +
    +
    +
    +

    Public corpora

    +
    +
    +
    +
    +
    + {% endblock page_content %}