diff --git a/app/corpora/forms.py b/app/corpora/forms.py index 8950622d1800a4cf48bc558264dbc5a2db9fb8f7..8403e621932deac8779a6f1b4fc27f9322984a29 100644 --- a/app/corpora/forms.py +++ b/app/corpora/forms.py @@ -78,9 +78,6 @@ 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 5c857e6186edaa1a6a7d1498fc4f247c1a6fa32a..69086bc049821abaa93b05245d52bdc883dcd600 100644 --- a/app/corpora/routes.py +++ b/app/corpora/routes.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta +from datetime import datetime from flask import ( abort, current_app, @@ -15,19 +15,121 @@ from threading import Thread import jwt import os from app import db, hashids -from app.models import Corpus, CorpusFile, CorpusStatus, CorpusFollowerAssociation, User +from app.models import Corpus, CorpusFile, CorpusStatus, User from . import bp -from .forms import ChangeCorpusSettingsForm, CreateCorpusFileForm, CreateCorpusForm, UpdateCorpusFileForm +from .forms import ( + CreateCorpusFileForm, + CreateCorpusForm, + UpdateCorpusFileForm +) + + +# @bp.route('/share/<token>', methods=['GET', 'POST']) +# def share_corpus(token): +# try: +# payload = jwt.decode( +# token, +# current_app.config['SECRET_KEY'], +# algorithms=['HS256'], +# issuer=current_app.config['SERVER_NAME'], +# options={'require': ['iat', 'iss', 'sub']} +# ) +# except jwt.PyJWTError: +# return False +# corpus_hashid = payload.get('sub') +# corpus_id = hashids.decode(corpus_hashid) +# return redirect(url_for('.corpus', corpus_id=corpus_id)) + + +@bp.route('/<hashid:corpus_id>/enable_is_public', methods=['POST']) +@login_required +def enable_corpus_is_public(corpus_id): + corpus = Corpus.query.get_or_404(corpus_id) + if not (corpus.user == current_user or current_user.is_administrator()): + abort(403) + corpus.is_public = True + db.session.commit() + return '', 204 + + +@bp.route('/<hashid:corpus_id>/disable_is_public', methods=['POST']) +@login_required +def disable_corpus_is_public(corpus_id): + corpus = Corpus.query.get_or_404(corpus_id) + if not (corpus.user == current_user or current_user.is_administrator()): + abort(403) + corpus.is_public = False + db.session.commit() + return '', 204 + + +# @bp.route('/<hashid:corpus_id>/follow', methods=['GET', 'POST']) +# @login_required +# 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() +# flash(f'You are following {corpus.title} now', category='corpus') +# return {}, 202 -@bp.route('') +@bp.route('/<hashid:corpus_id>/unfollow', methods=['GET', 'POST']) @login_required -def corpora(): - query = Corpus.query.filter( - (Corpus.user_id == current_user.id) | (Corpus.is_public == True) +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 + elif current_user.is_administrator(): + user_id = hashids.decode(user_hashid) + user = User.query.get_or_404(user_id) + else: + abort(403) + if user.is_following_corpus(corpus): + user.unfollow_corpus(corpus) + db.session.commit() + flash(f'You are not following {corpus.title} anymore', category='corpus') + return {}, 202 + + +# @bp.route('/add_permission/<hashid:corpus_id>/<hashid:user_id>/<int: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/<hashid:corpus_id>/<hashid:user_id>/<int: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' + + +@bp.route('/public') +@login_required +def public_corpora(): + corpora = [ + c.to_json_serializeable() + for c in Corpus.query.filter(Corpus.is_public == True).all() + ] + return render_template( + 'corpora/public_corpora.html.j2', + corpora=corpora, + title='Corpora' ) - corpora = [c.to_json_serializeable() for c in query.all()] - return render_template('corpora/corpora.html.j2', corpora=corpora, title='Corpora') @bp.route('/create', methods=['GET', 'POST']) @@ -60,83 +162,33 @@ def create_corpus(): @login_required def 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) - or corpus.is_public): - 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)) - now = datetime.utcnow() - payload = { - 'iat': now, - 'iss': current_app.config['SERVER_NAME'], - 'sub': corpus.hashid - } - token = jwt.encode( - payload, - current_app.config['SECRET_KEY'], - algorithm='HS256' - ) - if corpus.user == current_user: + if corpus.user == current_user or current_user.is_administrator(): + # now = datetime.utcnow() + # payload = { + # 'iat': now, + # 'iss': current_app.config['SERVER_NAME'], + # 'sub': corpus.hashid + # } + # token = jwt.encode( + # payload, + # current_app.config['SECRET_KEY'], + # algorithm='HS256' + # ) return render_template( 'corpora/corpus.html.j2', corpus=corpus, - token=token, + # token=token, title='Corpus' ) - else: + if current_user.is_following_corpus(corpus) or corpus.is_public: corpus_files = [x.to_json_serializeable() for x in corpus.files] return render_template( - 'corpora/corpus_public.html.j2', + 'corpora/public_corpus.html.j2', corpus=corpus, corpus_files=corpus_files, title='Corpus' ) - -@bp.route('/share/<token>', methods=['GET', 'POST']) -def share_corpus(token): - try: - payload = jwt.decode( - token, - current_app.config['SECRET_KEY'], - algorithms=['HS256'], - issuer=current_app.config['SERVER_NAME'], - options={'require': ['iat', 'iss', 'sub']} - ) - except jwt.PyJWTError: - return False - corpus_hashid = payload.get('sub') - corpus_id = hashids.decode(corpus_hashid) - return redirect(url_for('.corpus', corpus_id=corpus_id)) - -@bp.route('/<hashid:corpus_id>/enable_is_public', methods=['POST']) -@login_required -def enable_corpus_is_public(corpus_id): - corpus = Corpus.query.get_or_404(corpus_id) - if not (corpus.user == current_user or current_user.is_administrator()): - abort(403) - corpus.is_public = True - db.session.commit() - return '', 204 - - -@bp.route('/<hashid:corpus_id>/disable_is_public', methods=['POST']) -@login_required -def disable_corpus_is_public(corpus_id): - corpus = Corpus.query.get_or_404(corpus_id) - if not (corpus.user == current_user or current_user.is_administrator()): - abort(403) - corpus.is_public = False - db.session.commit() - return '', 204 + abort(403) @bp.route('/<hashid:corpus_id>', methods=['DELETE']) @@ -165,8 +217,7 @@ 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) - or corpus.is_public): + or current_user.is_following_corpus(corpus)): abort(403) return render_template( 'corpora/analyse_corpus.html.j2', @@ -315,57 +366,3 @@ def import_corpus(): @login_required def export_corpus(corpus_id): abort(503) - -@bp.route('/<hashid:corpus_id>/follow', methods=['GET', 'POST']) -@login_required -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() - flash(f'You are following {corpus.title} now', category='corpus') - return {}, 202 - -@bp.route('/<hashid:corpus_id>/unfollow', methods=['GET', 'POST']) -@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() - flash(f'You are not following {corpus.title} anymore', category='corpus') - return {}, 202 - -@bp.route('/add_permission/<hashid:corpus_id>/<hashid:user_id>/<int: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/<hashid:corpus_id>/<hashid:user_id>/<int: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/templates/corpora/corpus.html.j2 b/app/templates/corpora/corpus.html.j2 index 4edcb83835436eda1c85da160584f115f21a410d..34228347187cc93a25d29c617e3d7a7566ec2418 100644 --- a/app/templates/corpora/corpus.html.j2 +++ b/app/templates/corpora/corpus.html.j2 @@ -58,16 +58,6 @@ </div> </div> <div class="card-action"> - <div class="left-align"> - <div class="action-switch switch center-align" data-action="toggle-is-public"> - <span class="share"></span> - <label> - <input class="corpus-is-public" {% if corpus.is_public %}checked{% endif %} type="checkbox"> - <span class="lever"></span> - public - </label> - </div> - </div> <div class="right-align"> <a class="btn corpus-analyse-trigger disabled waves-effect waves-light" href="{{ url_for('corpora.analyse_corpus', corpus_id=corpus.id) }}"><i class="material-icons left">search</i>Analyze</a> <a class="action-button btn disabled waves-effect waves-light" data-action="build-request"><i class="nopaque-icons left">K</i>Build</a> @@ -89,17 +79,25 @@ </div> </div> </div> - {% if current_user.can(Permission.ADMINISTRATE) or current_user.hashid == corpus.user.hashid %} - <div class="col s12"> + + {# <div class="col s12"> <div class="card"> <div class="card-content"> + <div class="action-switch switch center-align" data-action="toggle-is-public"> + <span class="share"></span> + <label> + <input class="corpus-is-public" {% if corpus.is_public %}checked{% endif %} type="checkbox"> + <span class="lever"></span> + public + </label> + </div> + <a class="action-button btn waves-effect waves-light" id="generate-share-link-button">Generate Share Link</a> <div id="share-link"></div> <a class="action-button btn-small waves-effect waves-light hide" id="copy-share-link-button">Copy</a> </div> </div> </div> - {% endif %} <div class="col s12"> <div class="card"> @@ -108,7 +106,7 @@ <div class="user-list no-autoinit"></div> </div> </div> - </div> + </div> #} </div> </div> {% endblock page_content %} @@ -117,6 +115,8 @@ {{ super() }} <script> let corpusDisplay = new CorpusDisplay(document.querySelector('#corpus-display')); +</script> +{# <script> let generateShareLinkButton = document.querySelector('#generate-share-link-button'); let copyShareLinkButton = document.querySelector('#copy-share-link-button'); let shareLink = document.querySelector('#share-link'); @@ -136,5 +136,5 @@ document.execCommand('copy'); app.flash(`Copied!`, 'success'); }); -</script> +</script> #} {% endblock scripts %} diff --git a/app/templates/corpora/corpus_public.html.j2 b/app/templates/corpora/corpus_public.html.j2 deleted file mode 100644 index 1668e55c7bfbbfaaa5338aec2573032ce3179ec7..0000000000000000000000000000000000000000 --- a/app/templates/corpora/corpus_public.html.j2 +++ /dev/null @@ -1,108 +0,0 @@ -{% extends "base.html.j2" %} -{% import "materialize/wtf.html.j2" as wtf %} - -{% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %} - -{% block page_content %} -<div class="container"> - <div class="row"> - <div class="col s12"> - <h1>{{ corpus.title }} </h1> - <div class="row"> - <div class="col s8 m9 l10"> - <a class="btn waves-effect waves-light" id="follow-corpus-request"> - {% if current_user.is_following_corpus(corpus) %} - <i class="material-icons left">add</i>Unfollow Corpus - {% else %} - <i class="material-icons left">add</i>Follow Corpus - {% endif %} - </a> - {% if corpus.status.name in ['BUILT', 'STARTING_ANALYSIS_SESSION', 'RUNNING_ANALYSIS_SESSION', 'CANCELING_ANALYSIS_SESSION'] and current_user.is_following_corpus(corpus) %} - <a class="btn waves-effect waves-light" href="{{ url_for('corpora.analyse_corpus', corpus_id=corpus.id) }}">Analyse</a> - {% endif %} - </div> - </div> - <div class="card service-color-border border-darken" data-service="corpus-analysis" style="border-top: 10px solid"> - <div class="card-content"> - <div class="row"> - <div class="col s12"> - <p><b>Status:</b> <span class="chip corpus-status-text corpus-status-color white-text" data-status="{{ corpus.status.name }}"></span></p> - <p></p> - <br> - </div> - <div class="col s12"> - <p><b>Description:</b> {{ corpus.description }}</p> - <br> - <p></p> - </div> - <div class="col s6"> - <p><b>Creation date:</b> {{ corpus.creation_date }}</p> - </div> - <div class="col s6"> - <p><b>Number of tokens used:</b> {{ corpus.num_tokens }}</p> - </div> - </div> - </div> - </div> - <div class="card"> - <div class="card-content"> - <span class="card-title" id="files">Corpus files</span> - <div class="corpus-file-list no-autoinit" data-user-id="{{ corpus.user.hashid }}" data-corpus-id="{{ corpus.hashid }}"></div> - </div> - </div> - </div> - </div> - </div> -</div> -{% endblock page_content %} - -{% block scripts %} -{{ super() }} -<script> - let corpusFileList = new PublicCorpusFileList(document.querySelector('.corpus-file-list')); - corpusFileList.add({{ corpus_files|tojson }}); - - let corpusFollowingRequest = document.querySelector('#follow-corpus-request'); - - {# let followingUserList = new UserList(document.querySelector('.user-list')); - followingUserList.add({{ following_users|tojson }}); #} - - corpusFollowingRequest.addEventListener('click', () => { - if ({{ current_user.is_following_corpus(corpus)|tojson }}) { - return new Promise((resolve, reject) => { - fetch(`/corpora/{{ corpus.hashid }}/unfollow`, {method: 'POST', headers: {Accept: 'application/json'}}) - .then( - (response) => { - if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);} - if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);} - {# app.flash(`You are not following "{{ corpus.title }}" anymore`, 'corpus'); #} - resolve(response); - window.location.href = '{{ url_for("corpora.corpus", corpus_id=corpus.id) }}'; - }, - (response) => { - app.flash('Something went wrong', 'error'); - reject(response); - } - ); - }); - } else { - return new Promise((resolve, reject) => { - fetch(`/corpora/{{ corpus.hashid }}/follow`, {method: 'POST', headers: {Accept: 'application/json'}}) - .then( - (response) => { - if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);} - if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);} - {# app.flash(`You follow "{{ corpus.title }}" now`, 'corpus'); #} - window.location.href = '{{ url_for("corpora.corpus", corpus_id=corpus.id) }}'; - resolve(response); - }, - (response) => { - app.flash('Something went wrong', 'error'); - reject(response); - } - ); - }); - } - }); -</script> -{% endblock scripts %} diff --git a/app/templates/corpora/corpora.html.j2 b/app/templates/corpora/public_corpora.html.j2 similarity index 100% rename from app/templates/corpora/corpora.html.j2 rename to app/templates/corpora/public_corpora.html.j2 diff --git a/app/templates/corpora/public_corpus.html.j2 b/app/templates/corpora/public_corpus.html.j2 new file mode 100644 index 0000000000000000000000000000000000000000..ace8665f5add3ebab42a9e747735539287359df9 --- /dev/null +++ b/app/templates/corpora/public_corpus.html.j2 @@ -0,0 +1,80 @@ +{% extends "base.html.j2" %} +{% import "materialize/wtf.html.j2" as wtf %} + +{% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %} + +{% block page_content %} +<div class="container"> + <div class="row"> + <div class="col s12"> + <h1>{{ corpus.title }} </h1> + <div class="row"> + <div class="col s8 m9 l10"> + {% if current_user.is_following_corpus(corpus) %} + <a class="action-button btn waves-effect waves-light" data-action="unfollow-request"><i class="material-icons left">add</i>Unfollow Corpus</a> + {% endif %} + {% if corpus.status.name in ['BUILT', 'STARTING_ANALYSIS_SESSION', 'RUNNING_ANALYSIS_SESSION', 'CANCELING_ANALYSIS_SESSION'] and current_user.is_following_corpus(corpus) %} + <a class="btn waves-effect waves-light" href="{{ url_for('corpora.analyse_corpus', corpus_id=corpus.id) }}">Analyse</a> + {% endif %} + </div> + </div> + <div class="card service-color-border border-darken" data-service="corpus-analysis" style="border-top: 10px solid"> + <div class="card-content"> + <div class="row"> + <div class="col s12"> + <p><b>Status:</b> <span class="chip corpus-status-text corpus-status-color white-text" data-status="{{ corpus.status.name }}"></span></p> + <p></p> + <br> + </div> + <div class="col s12"> + <p><b>Description:</b> {{ corpus.description }}</p> + <br> + <p></p> + </div> + <div class="col s6"> + <p><b>Creation date:</b> {{ corpus.creation_date }}</p> + </div> + <div class="col s6"> + <p><b>Number of tokens used:</b> {{ corpus.num_tokens }}</p> + </div> + </div> + </div> + </div> + <div class="card"> + <div class="card-content"> + <span class="card-title" id="files">Corpus files</span> + <div class="corpus-file-list no-autoinit" data-user-id="{{ corpus.user.hashid }}" data-corpus-id="{{ corpus.hashid }}"></div> + </div> + </div> + </div> + </div> + </div> +</div> +{% endblock page_content %} + +{% block scripts %} +{{ super() }} +<script> + let corpusFileList = new PublicCorpusFileList(document.querySelector('.corpus-file-list')); + corpusFileList.add({{ corpus_files|tojson }}); + + let unfollowRequestElement = document.querySelector('.action-button[data-action="unfollow-request"]'); + unfollowRequestElement.addEventListener('click', () => { + return new Promise((resolve, reject) => { + fetch('{{ url_for("corpora.unfollow_corpus", corpus_id=corpus.id) }}', {method: 'POST', headers: {Accept: 'application/json'}}) + .then( + (response) => { + if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);} + if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);} + resolve(response); + window.location.href = '{{ url_for("corpora.corpus", corpus_id=corpus.id) }}'; + }, + (response) => { + app.flash('Something went wrong', 'error'); + reject(response); + } + ); + }); + }); +</script> +{% endblock scripts %}