From 6c76d27a3214607e7ab28de42468965adf461546 Mon Sep 17 00:00:00 2001 From: Inga Kirschnick <inga.kirschnick@uni-bielefeld.de> Date: Thu, 27 Apr 2023 15:11:18 +0200 Subject: [PATCH] Update corpus page --- app/corpora/files/json_routes.py | 2 +- app/corpora/files/routes.py | 4 +- app/corpora/followers/json_routes.py | 36 ++-- app/corpora/json_routes.py | 4 +- app/corpora/routes.py | 36 ++-- app/models.py | 24 +-- app/static/js/ResourceLists/CorpusFileList.js | 30 +-- .../js/ResourceLists/CorpusFollowerList.js | 4 +- .../js/ResourceLists/PublicCorpusFileList.js | 15 -- app/templates/_scripts.html.j2 | 1 - .../concordance.html.j2 | 0 .../reader.html.j2 | 0 .../template.html.j2 | 0 .../corpora/_corpus/action_buttons.html.j2 | 36 ++++ .../_corpus/corpus_information_card.html.j2 | 72 +++++++ app/templates/corpora/analysis.html.j2 | 4 +- app/templates/corpora/corpus.html.j2 | 179 +++++++++++------- app/templates/corpora/public_corpus.html.j2 | 122 ------------ 18 files changed, 296 insertions(+), 273 deletions(-) delete mode 100644 app/static/js/ResourceLists/PublicCorpusFileList.js rename app/templates/corpora/{analysis_extensions => _analysis}/concordance.html.j2 (100%) rename app/templates/corpora/{analysis_extensions => _analysis}/reader.html.j2 (100%) rename app/templates/corpora/{analysis_extensions => _analysis}/template.html.j2 (100%) create mode 100644 app/templates/corpora/_corpus/action_buttons.html.j2 create mode 100644 app/templates/corpora/_corpus/corpus_information_card.html.j2 delete mode 100644 app/templates/corpora/public_corpus.html.j2 diff --git a/app/corpora/files/json_routes.py b/app/corpora/files/json_routes.py index e4f06084..f8d5ddb4 100644 --- a/app/corpora/files/json_routes.py +++ b/app/corpora/files/json_routes.py @@ -8,7 +8,7 @@ from . import bp @bp.route('/<hashid:corpus_id>/files/<hashid:corpus_file_id>', methods=['DELETE']) -@corpus_follower_permission_required('REMOVE_CORPUS_FILE') +@corpus_follower_permission_required('MANAGE_FILES') @content_negotiation(produces='application/json') def delete_corpus_file(corpus_id, corpus_file_id): def _delete_corpus_file(app, corpus_file_id): diff --git a/app/corpora/files/routes.py b/app/corpora/files/routes.py index 1df7c9bf..e5ad094d 100644 --- a/app/corpora/files/routes.py +++ b/app/corpora/files/routes.py @@ -25,7 +25,7 @@ def corpus_files(corpus_id): @bp.route('/<hashid:corpus_id>/files/create', methods=['GET', 'POST']) @register_breadcrumb(bp, '.entity.files.create', 'Create', endpoint_arguments_constructor=corpus_eac) -@corpus_follower_permission_required('ADD_CORPUS_FILE') +@corpus_follower_permission_required('MANAGE_FILES') def create_corpus_file(corpus_id): corpus = Corpus.query.get_or_404(corpus_id) form = CreateCorpusFileForm() @@ -67,7 +67,7 @@ def create_corpus_file(corpus_id): @bp.route('/<hashid:corpus_id>/files/<hashid:corpus_file_id>', methods=['GET', 'POST']) @register_breadcrumb(bp, '.entity.files.entity', '', dynamic_list_constructor=corpus_file_dlc) -@corpus_follower_permission_required('UPDATE_CORPUS_FILE') +@corpus_follower_permission_required('MANAGE_FILES') 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() form = UpdateCorpusFileForm(data=corpus_file.to_json_serializeable()) diff --git a/app/corpora/followers/json_routes.py b/app/corpora/followers/json_routes.py index f8517359..9b31f8ee 100644 --- a/app/corpora/followers/json_routes.py +++ b/app/corpora/followers/json_routes.py @@ -1,4 +1,5 @@ -from flask import abort, request +from flask import abort, flash, jsonify, make_response, request +from flask_login import current_user from app import db from app.decorators import content_negotiation from app.models import ( @@ -10,9 +11,8 @@ from app.models import ( from ..decorators import corpus_follower_permission_required from . import bp - @bp.route('/<hashid:corpus_id>/followers', methods=['POST']) -@corpus_follower_permission_required('ADD_FOLLOWER') +@corpus_follower_permission_required('MANAGE_FOLLOWERS') @content_negotiation(consumes='application/json', produces='application/json') def create_corpus_followers(corpus_id): usernames = request.json @@ -31,7 +31,7 @@ def create_corpus_followers(corpus_id): @bp.route('/<hashid:corpus_id>/followers/<hashid:follower_id>/role', methods=['PUT']) -@corpus_follower_permission_required('UPDATE_FOLLOWER') +@corpus_follower_permission_required('MANAGE_FOLLOWERS') @content_negotiation(consumes='application/json', produces='application/json') def update_corpus_follower_role(corpus_id, follower_id): role_name = request.json @@ -51,17 +51,25 @@ def update_corpus_follower_role(corpus_id, follower_id): @bp.route('/<hashid:corpus_id>/followers/<hashid:follower_id>', methods=['DELETE']) -@corpus_follower_permission_required('REMOVE_FOLLOWER') -@content_negotiation(produces='application/json') def delete_corpus_follower(corpus_id, follower_id): cfa = CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id, follower_id=follower_id).first_or_404() + if not ( + current_user.id == follower_id + or current_user == cfa.corpus.user + or CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id, follower_id=current_user.id).first().role.has_permission('MANAGE_FOLLOWERS') + or current_user.is_administrator()): + abort(403) + if current_user.id == follower_id: + flash(f'You are no longer following "{cfa.corpus.title}"', 'corpus') + response = make_response() + response.status_code = 204 + else: + response_data = { + 'message': f'"{cfa.follower.username}" is not following "{cfa.corpus.title}" anymore', + 'category': 'corpus' + } + response = jsonify(response_data) + response.status_code = 200 cfa.follower.unfollow_corpus(cfa.corpus) db.session.commit() - response_data = { - 'message': ( - f'"{cfa.follower.username}" is not following ' - f'"{cfa.corpus.title}" anymore' - ), - 'category': 'corpus' - } - return response_data, 200 + return response diff --git a/app/corpora/json_routes.py b/app/corpora/json_routes.py index e73281ed..908f1604 100644 --- a/app/corpora/json_routes.py +++ b/app/corpora/json_routes.py @@ -33,7 +33,7 @@ def delete_corpus(corpus_id): @bp.route('/<hashid:corpus_id>/build', methods=['POST']) -@corpus_owner_or_admin_required +@corpus_follower_permission_required('MANAGE_FILES') @content_negotiation(produces='application/json') def build_corpus(corpus_id): def _build_corpus(app, corpus_id): @@ -58,7 +58,7 @@ def build_corpus(corpus_id): @bp.route('/<hashid:corpus_id>/generate-share-link', methods=['POST']) -@corpus_follower_permission_required('ADD_FOLLOWER') +@corpus_follower_permission_required('MANAGE_FOLLOWERS') @content_negotiation(consumes='application/json', produces='application/json') def generate_corpus_share_link(corpus_id): data = request.json diff --git a/app/corpora/routes.py b/app/corpora/routes.py index 75f7dfc0..8ef108b4 100644 --- a/app/corpora/routes.py +++ b/app/corpora/routes.py @@ -50,26 +50,24 @@ def create_corpus(): @register_breadcrumb(bp, '.entity', '', dynamic_list_constructor=corpus_dlc) def corpus(corpus_id): corpus = Corpus.query.get_or_404(corpus_id) - corpus_follower_roles = CorpusFollowerRole.query.all() + cfrs = CorpusFollowerRole.query.all() users = User.query.filter(User.is_public == True, User.id != current_user.id).all() - # TODO: Add URL query option to toggle view - if corpus.user == current_user or current_user.is_administrator(): - return render_template( - 'corpora/corpus.html.j2', - title=corpus.title, - corpus=corpus, - corpus_follower_roles=corpus_follower_roles, - users = users - ) - if current_user.is_following_corpus(corpus) or corpus.is_public: - cfa = CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id, follower_id=current_user.id).first_or_404() - return render_template( - 'corpora/public_corpus.html.j2', - title=corpus.title, - corpus=corpus, - cfa=cfa - ) - abort(403) + cfa = CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id, follower_id=current_user.id).first() + if cfa is None: + if corpus.user == current_user or current_user.is_administrator(): + cfr = CorpusFollowerRole.query.filter_by(name='Administrator').first() + else: + cfr = CorpusFollowerRole.query.filter_by(name='Anonymous').first() + else: + cfr = cfa.role + return render_template( + 'corpora/corpus.html.j2', + title=corpus.title, + corpus=corpus, + cfrs=cfrs, + cfr=cfr, + users = users + ) @bp.route('/<hashid:corpus_id>/analysis') diff --git a/app/models.py b/app/models.py index 8bbfb665..2ee313ec 100644 --- a/app/models.py +++ b/app/models.py @@ -113,12 +113,9 @@ class ProfilePrivacySettings(IntEnum): class CorpusFollowerPermission(IntEnum): VIEW = 1 - ADD_CORPUS_FILE = 2 - UPDATE_CORPUS_FILE = 4 - REMOVE_CORPUS_FILE = 8 - ADD_FOLLOWER = 16 - UPDATE_FOLLOWER = 32 - REMOVE_FOLLOWER = 64 + MANAGE_FILES = 2 + MANAGE_FOLLOWERS = 4 + MANAGE_CORPUS = 8 @staticmethod def get(corpus_follower_permission: Union['CorpusFollowerPermission', int, str]) -> 'CorpusFollowerPermission': @@ -434,23 +431,20 @@ class CorpusFollowerRole(HashidMixin, db.Model): @staticmethod def insert_defaults(): roles = { + 'Anonymous': [], 'Viewer': [ CorpusFollowerPermission.VIEW ], 'Contributor': [ CorpusFollowerPermission.VIEW, - CorpusFollowerPermission.ADD_CORPUS_FILE, - CorpusFollowerPermission.UPDATE_CORPUS_FILE, - CorpusFollowerPermission.REMOVE_CORPUS_FILE + CorpusFollowerPermission.MANAGE_FILES ], 'Administrator': [ CorpusFollowerPermission.VIEW, - CorpusFollowerPermission.ADD_CORPUS_FILE, - CorpusFollowerPermission.UPDATE_CORPUS_FILE, - CorpusFollowerPermission.REMOVE_CORPUS_FILE, - CorpusFollowerPermission.ADD_FOLLOWER, - CorpusFollowerPermission.UPDATE_FOLLOWER, - CorpusFollowerPermission.REMOVE_FOLLOWER + CorpusFollowerPermission.MANAGE_FILES, + CorpusFollowerPermission.MANAGE_FOLLOWERS, + CorpusFollowerPermission.MANAGE_CORPUS + ] } default_role_name = 'Viewer' diff --git a/app/static/js/ResourceLists/CorpusFileList.js b/app/static/js/ResourceLists/CorpusFileList.js index 0ce96e2d..813676fb 100644 --- a/app/static/js/ResourceLists/CorpusFileList.js +++ b/app/static/js/ResourceLists/CorpusFileList.js @@ -11,6 +11,8 @@ class CorpusFileList extends ResourceList { this.isInitialized = false; this.userId = listContainerElement.dataset.userId; this.corpusId = listContainerElement.dataset.corpusId; + this.hasPermissionView = listContainerElement.dataset?.hasPermissionView == 'true' || false; + this.hasPermissionManageFiles = listContainerElement.dataset?.hasPermissionManageFiles == 'true' || false; if (this.userId === undefined || this.corpusId === undefined) {return;} app.subscribeUser(this.userId).then((response) => { app.socket.on('PATCH', (patch) => { @@ -24,19 +26,21 @@ class CorpusFileList extends ResourceList { } get item() { - return ` - <tr class="list-item clickable hoverable"> - <td><span class="filename"></span></td> - <td><span class="author"></span></td> - <td><span class="title"></span></td> - <td><span class="publishing-year"></span></td> - <td class="right-align"> - <a class="list-action-trigger btn-floating red waves-effect waves-light" data-list-action="delete"><i class="material-icons">delete</i></a> - <a class="list-action-trigger btn-floating service-color darken waves-effect waves-light" data-list-action="download" data-service="corpus-analysis"><i class="material-icons">file_download</i></a> - <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(); + return (values) => { + return ` + <tr class="list-item"> + <td><span class="filename"></span></td> + <td><span class="author"></span></td> + <td><span class="title"></span></td> + <td><span class="publishing-year"></span></td> + <td class="right-align"> + <a class="list-action-trigger btn-floating red waves-effect waves-light ${this.hasPermissionManageFiles ? '' : 'hide'}" data-list-action="delete"><i class="material-icons">delete</i></a> + <a class="list-action-trigger btn-floating service-color darken waves-effect waves-light ${this.hasPermissionView ? '' : 'hide'}" data-list-action="download" data-service="corpus-analysis"><i class="material-icons">file_download</i></a> + <a class="list-action-trigger btn-floating service-color darken waves-effect waves-light ${this.hasPermissionManageFiles ? '' : 'hide'}" data-list-action="view" data-service="corpus-analysis"><i class="material-icons">send</i></a> + </td> + </tr> + `.trim(); + } } get valueNames() { diff --git a/app/static/js/ResourceLists/CorpusFollowerList.js b/app/static/js/ResourceLists/CorpusFollowerList.js index 82629354..1d0b4877 100644 --- a/app/static/js/ResourceLists/CorpusFollowerList.js +++ b/app/static/js/ResourceLists/CorpusFollowerList.js @@ -22,7 +22,9 @@ class CorpusFollowerList extends ResourceList { }); }); app.getUser(this.userId).then((user) => { - this.add(Object.values(user.corpora[this.corpusId].corpus_follower_associations)); + let corpusFollowerAssociations = Object.values(user.corpora[this.corpusId].corpus_follower_associations); + let filteredList = corpusFollowerAssociations.filter(association => association.follower.id != currentUserId); + this.add(filteredList); this.isInitialized = true; }); } diff --git a/app/static/js/ResourceLists/PublicCorpusFileList.js b/app/static/js/ResourceLists/PublicCorpusFileList.js deleted file mode 100644 index 37a284cf..00000000 --- a/app/static/js/ResourceLists/PublicCorpusFileList.js +++ /dev/null @@ -1,15 +0,0 @@ -class PublicCorpusFileList extends CorpusFileList { - get item() { - return ` - <tr class="list-item clickable hoverable"> - <td><span class="filename"></span></td> - <td><span class="author"></span></td> - <td><span class="title"></span></td> - <td><span class="publishing-year"></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(); - } -} diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2 index f893e898..07d2d59a 100644 --- a/app/templates/_scripts.html.j2 +++ b/app/templates/_scripts.html.j2 @@ -39,7 +39,6 @@ output='gen/ResourceLists.%(version)s.js', 'js/ResourceLists/ResourceList.js', 'js/ResourceLists/CorpusFileList.js', - 'js/ResourceLists/PublicCorpusFileList.js', 'js/ResourceLists/CorpusList.js', 'js/ResourceLists/FollowedCorpusList.js', 'js/ResourceLists/PublicCorpusList.js', diff --git a/app/templates/corpora/analysis_extensions/concordance.html.j2 b/app/templates/corpora/_analysis/concordance.html.j2 similarity index 100% rename from app/templates/corpora/analysis_extensions/concordance.html.j2 rename to app/templates/corpora/_analysis/concordance.html.j2 diff --git a/app/templates/corpora/analysis_extensions/reader.html.j2 b/app/templates/corpora/_analysis/reader.html.j2 similarity index 100% rename from app/templates/corpora/analysis_extensions/reader.html.j2 rename to app/templates/corpora/_analysis/reader.html.j2 diff --git a/app/templates/corpora/analysis_extensions/template.html.j2 b/app/templates/corpora/_analysis/template.html.j2 similarity index 100% rename from app/templates/corpora/analysis_extensions/template.html.j2 rename to app/templates/corpora/_analysis/template.html.j2 diff --git a/app/templates/corpora/_corpus/action_buttons.html.j2 b/app/templates/corpora/_corpus/action_buttons.html.j2 new file mode 100644 index 00000000..1620d177 --- /dev/null +++ b/app/templates/corpora/_corpus/action_buttons.html.j2 @@ -0,0 +1,36 @@ +{% set owner_admin %} + <div class="col s12 l6" style="padding: 0 2.5px;"> + <a class="action-button btn disabled waves-effect waves-light" data-action="build-request" style="width: 100%;"><i class="nopaque-icons left">K</i>Build</a> + </div> + <div class="col s12 l6" style="padding: 0 2.5px;"> + <a class="action-button btn disabled waves-effect waves-light" data-action="analyze" href="{{ url_for('corpora.analysis', corpus_id=corpus.id) }}" style="width: 100%;"><i class="material-icons left">search</i>Analyze</a> + </div> + <div class="col s12 l6" style="padding: 5px 2.5px 0 2.5px;"> + <a class="btn waves-effect waves-light modal-trigger" href="#publishing-modal" style="width: 100%;"><i class="material-icons left">publish</i>Publishing</a> + </div> + <div class="col s12 l6" style="padding: 5px 2.5px 0 2.5px;"> + <a class="btn red waves-effect waves-light modal-trigger" href="#delete-modal" style="width: 100%;"><i class="material-icons left">delete</i>Delete</a> + </div> +{% endset %} + +{% set follower %} + {% if cfr.has_permission('MANAGE_FILES') %} + <div class="col s12 l6" style="padding: 0 2.5px;"> + {% if corpus.status.name in ['UNPREPARED'] %} + <a class="action-button btn waves-effect waves-light" id="build-button" style="width: 100%;"><i class="nopaque-icons left">K</i>Build</a> + {% else %} + <a class="action-button btn waves-effect waves-light disabled" id="build-button" style="width: 100%;"><i class="nopaque-icons left">K</i>Build</a> + {% endif %} + </div> + {% if corpus.status.name in ['BUILT', 'STARTING_ANALYSIS_SESSION', 'RUNNING_ANALYSIS_SESSION', 'CANCELING_ANALYSIS_SESSION'] %} + <div class="col s12 l6" style="padding: 0 2.5px;"> + <a class="btn waves-effect waves-light" href="{{ url_for('corpora.analysis', corpus_id=corpus.id) }}" style="width: 100%;"><i class="material-icons left">search</i>Analyze</a> + </div> + {% endif %} + {% endif %} + {% if current_user.is_following_corpus(corpus) %} + <div class="col s12 l6" style="padding: 5px 2.5px 0 2.5px;"> + <a class="action-button btn red waves-effect waves-light" data-action="unfollow-request" style="width: 100%;"><i class="material-icons left outlined">close</i>Unfollow Corpus</a> + </div> + {% endif %} +{% endset %} diff --git a/app/templates/corpora/_corpus/corpus_information_card.html.j2 b/app/templates/corpora/_corpus/corpus_information_card.html.j2 new file mode 100644 index 00000000..bbaa2cee --- /dev/null +++ b/app/templates/corpora/_corpus/corpus_information_card.html.j2 @@ -0,0 +1,72 @@ +{% set name = 'Corpus Information' %} + +{% set owner %} +<div class="col s12 l7"> + <div class="card service-color-border border-darken" data-service="corpus-analysis" style="border-top: 10px solid"> + <div class="card-content"> + <span class="chip corpus-status corpus-status-color corpus-status-text white-text"></span> + <div class="active preloader-wrapper small corpus-status-spinner"> + <div class="spinner-layer spinner-blue-only"> + <div class="circle-clipper left"> + <div class="circle"></div> + </div> + <div class="gap-patch"> + <div class="circle"></div> + </div> + <div class="circle-clipper right"> + <div class="circle"></div> + </div> + </div> + </div> + <div class="row"> + <div class="col s12"> + <div class="input-field"> + <input class="corpus-description" disabled id="corpus-description" type="text"> + <label for="corpus-description">Description</label> + </div> + </div> + + <div class="col s12 m6"> + <div class="input-field"> + <input class="corpus-creation-date validate" disabled id="corpus-creation-date" type="text"> + <label for="corpus-creation-date">Creation date</label> + </div> + </div> + + <div class="col s12 m6"> + <div class="input-field"> + <input class="corpus-token-ratio validate" disabled id="corpus-token-ratio" type="text"> + <label for="corpus-token-ratio">Nr. of tokens used <sup><i class="material-icons tooltipped tiny" data-position="bottom" data-tooltip="Current number of tokens in this corpus. Updates after every analyze session.">help</i></sup></label> + </div> + </div> + </div> + </div> + </div> +</div> +{% endset %} + +{% set public %} +<div class="col s12 l7"> + <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> + <br> + </div> + <div class="col s12"> + <p><b>Description:</b> {{ corpus.description }}</p> + <br> + </div> + <div class="col s12"> + <p><b>Creation date:</b> {{ corpus.creation_date }}</p> + <br> + </div> + <div class="col s12"> + <p><b>Number of tokens used:</b> {{ corpus.num_tokens }}</p> + </div> + </div> + </div> + </div> +</div> +{% endset %} diff --git a/app/templates/corpora/analysis.html.j2 b/app/templates/corpora/analysis.html.j2 index 757e3a54..cbfd6a6a 100644 --- a/app/templates/corpora/analysis.html.j2 +++ b/app/templates/corpora/analysis.html.j2 @@ -1,7 +1,7 @@ {% extends "base.html.j2" %} {% import "materialize/wtf.html.j2" as wtf %} -{% import 'corpora/analysis_extensions/concordance.html.j2' as concordance_extension %} -{% import 'corpora/analysis_extensions/reader.html.j2' as reader_extension %} +{% import 'corpora/_analysis/concordance.html.j2' as concordance_extension %} +{% import 'corpora/_analysis/reader.html.j2' as reader_extension %} {% set extensions = [concordance_extension, reader_extension] %} {% block main_attribs %} class="service-scheme" data-service="corpus-analysis" id="corpus-analysis-app-container"{% endblock main_attribs %} diff --git a/app/templates/corpora/corpus.html.j2 b/app/templates/corpora/corpus.html.j2 index 20f66b8f..02d4245d 100644 --- a/app/templates/corpora/corpus.html.j2 +++ b/app/templates/corpora/corpus.html.j2 @@ -1,5 +1,7 @@ {% extends "base.html.j2" %} {% import "materialize/wtf.html.j2" as wtf %} +{% import "corpora/_corpus/corpus_information_card.html.j2" as corpus_information_card with context %} +{% import "corpora/_corpus/action_buttons.html.j2" as action_buttons with context %} {% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %} @@ -7,69 +9,28 @@ <div class="container"> <div class="row" data-corpus-id="{{ corpus.hashid }}" data-user-id="{{ corpus.user.hashid }}" id="corpus-display"> <div class="col s12"> - <h1 id="title"><span class="corpus-title"></span></h1> - </div> - <div class="col s12 l7"> - <div class="card service-color-border border-darken" data-service="corpus-analysis" style="border-top: 10px solid"> - <div class="card-content"> - <span class="chip corpus-status corpus-status-color corpus-status-text white-text"></span> - <div class="active preloader-wrapper small corpus-status-spinner"> - <div class="spinner-layer spinner-blue-only"> - <div class="circle-clipper left"> - <div class="circle"></div> - </div> - <div class="gap-patch"> - <div class="circle"></div> - </div> - <div class="circle-clipper right"> - <div class="circle"></div> - </div> - </div> - </div> - <div class="row"> - <div class="col s12"> - <div class="input-field"> - <input class="corpus-description" disabled id="corpus-description" type="text"> - <label for="corpus-description">Description</label> - </div> - </div> - - <div class="col s12 m6"> - <div class="input-field"> - <input class="corpus-creation-date validate" disabled id="corpus-creation-date" type="text"> - <label for="corpus-creation-date">Creation date</label> - </div> - </div> - - <div class="col s12 m6"> - <div class="input-field"> - <input class="corpus-token-ratio validate" disabled id="corpus-token-ratio" type="text"> - <label for="corpus-token-ratio">Nr. of tokens used <sup><i class="material-icons tooltipped tiny" data-position="bottom" data-tooltip="Current number of tokens in this corpus. Updates after every analyze session.">help</i></sup></label> - </div> - </div> - </div> - </div> - </div> + <h1>{{ corpus.title }}</h1> </div> + {% if current_user == corpus.user or current_user.is_administrator() %} + {{ corpus_information_card.owner }} + {% else %} + {{ corpus_information_card.public }} + {% endif %} + + {% if cfr.has_permission('VIEW') %} <div class="col s12 l5"> <div class="card"> <div class="card-content"> <span class="card-title">Actions</span> <div class="row"> - <div class="col s12 l6" style="padding: 0 2.5px;"> - <a class="action-button btn disabled waves-effect waves-light" data-action="build-request" style="width: 100%;"><i class="nopaque-icons left">K</i>Build</a> - </div> - <div class="col s12 l6" style="padding: 0 2.5px;"> - <a class="action-button btn disabled waves-effect waves-light" data-action="analyze" href="{{ url_for('corpora.analysis', corpus_id=corpus.id) }}" style="width: 100%;"><i class="material-icons left">search</i>Analyze</a> - </div> - <div class="col s12 l6" style="padding: 5px 2.5px 0 2.5px;"> - <a class="btn waves-effect waves-light modal-trigger" href="#publishing-modal" style="width: 100%;"><i class="material-icons left">publish</i>Publishing</a> - </div> - <div class="col s12 l6" style="padding: 5px 2.5px 0 2.5px;"> - <a class="btn red waves-effect waves-light modal-trigger" href="#delete-modal" style="width: 100%;"><i class="material-icons left">delete</i>Delete</a> - </div> + {% if current_user == corpus.user or current_user.is_administrator() %} + {{ action_buttons.owner_admin }} + {% else %} + {{ action_buttons.follower }} + {% endif %} </div> + {% if cfr.has_permission('MANAGE_FOLLOWERS') %} <span class="card-title">Social</span> <div class="row"> <div class="col s12 l6" style="padding: 0 2.5px;"> @@ -79,24 +40,66 @@ <a class="btn waves-effect waves-light modal-trigger" href="#share-link-modal" style="width: 100%;"><i class="material-icons left">link</i>Share link</a> </div> </div> + {% endif %} </div> </div> </div> + {% endif %} + + {% if current_user != corpus.user %} + <div class="col s12"> + <div class="card"> + <div class="card-content"> + <span class="card-title" id="files">Corpus Owner</span> + <div class="row"> + <div class="col s12"> + <table> + <tr> + <td style="width:10%; margin-top:25px;"> + <img src="{{ url_for('users.user_avatar', user_id=corpus.user.id) }}" alt="user-image" class="circle responsive-img"> + </td> + <td></td> + <td> + <ul> + <li><b>{{ corpus.user.username }}</b></li> + {% if corpus.user.full_name %} + <li>{{ corpus.user.full_name }}</li> + {% endif %} + {% if corpus.user.show_email %} + <li></li><a href="mailto:{{ corpus.user.email }}">{{ corpus.user.email }}</a></li> + {% endif %} + </ul> + </td> + </tr> + </table> + <br> + <p></p> + {% if not current_user.is_following_corpus(corpus) %} + <a class="waves-effect waves-light btn-small">Request Corpus</a> + {% endif %} + <a class="waves-effect waves-light btn-small" href="{{ url_for('users.user', user_id=corpus.user.id) }}">View profile</a> + </div> + </div> + </div> + </div> + </div> + {% endif %} <div class="col s12"> <div class="card"> <div class="card-content"> <span class="card-title" id="files">Corpus files</span> - <div class="corpus-file-list" data-user-id="{{ corpus.user.hashid }}" data-corpus-id="{{ corpus.hashid }}"></div> + <div class="corpus-file-list" id="corpus-file-list" data-has-permission-view="{{ cfr.has_permission('VIEW')|tojson }}" data-has-permission-manage-files="{{ cfr.has_permission('MANAGE_FILES')|tojson }}" data-corpus-id="{{ corpus.hashid }}"></div> </div> + {% if cfr.has_permission('MANAGE_FILES') %} <div class="card-action right-align"> <a href="{{ url_for('corpora.create_corpus_file', corpus_id=corpus.id) }}" class="btn waves-effect waves-light"><i class="material-icons left">add</i>Add corpus file</a> </div> + {% endif %} </div> </div> - <div class="col s12"></div> - + {% if cfr.has_permission('MANAGE_FOLLOWERS') %} <div class="col s12"> <div class="card"> <div class="card-content"> @@ -105,12 +108,16 @@ </div> </div> </div> + {% endif %} + </div> </div> {% endblock page_content %} {% block modals %} {{ super() }} + +{% if cfr.has_permission('MANAGE_FOLLOWERS') %} <div class="modal" id="publishing-modal"> <div class="modal-content"> <h4>Change your Corpus publishing status</h4> @@ -129,7 +136,9 @@ <a class="modal-close waves-effect waves-green btn-flat">Close</a> </div> </div> +{% endif %} +{% if current_user == corpus.user or current_user.is_administrator() %} <div class="modal" id="delete-modal"> <div class="modal-content"> <h4>Confirm Corpus deletion</h4> @@ -140,7 +149,9 @@ <a class="btn modal-close red waves-effect waves-light" id="delete-modal-delete-button">Delete</a> </div> </div> +{% endif %} +{% if cfr.has_permission('MANAGE_FOLLOWERS') %} <div class="modal no-autoinit" id="invite-user-modal"> <div class="modal-content"> <h4>Invite a nopaque user by username</h4> @@ -174,27 +185,27 @@ You can set different roles via the link, you can also edit them later in the menu below. It is recommended not to set the expiration date of the link too far. </p> - <h5>HIER RECHTLICHEN HINWEIS EINFÃœGEN</h5> + <h5>Please make sure that the invited users are legally allowed to view the included corpus files.</h5> <div class="row"> - <div class="col s12 l2"> + <div class="col s12 l3"> <div class="input-field"> <i class="material-icons prefix">badge</i> <select id="share-link-modal-corpus-follower-role-select"> - {% for corpus_follower_role in corpus_follower_roles %} - <option value="{{ corpus_follower_role.name }}">{{ corpus_follower_role.name }}</option> + {% for cfr in cfrs %} + <option value="{{ cfr.name }}">{{ cfr.name }}</option> {% endfor %} </select> <label>Role</label> </div> </div> - <div class="col s12 l2"> + <div class="col s12 l3"> <div class="input-field"> <i class="material-icons prefix">calendar_month</i> <input type="text" class="datepicker no-autoinit" id="share-link-modal-expiration-date-datepicker"> <label for="expiration-date">Expiration date</label> </div> </div> - <div class="col s12 l2"> + <div class="col s12 l3"> <br class="hide-on-med-and-down"> <a class="btn waves-effect waves-light" id="share-link-modal-create-button">Create<i class="material-icons right">send</i></a> </div> @@ -218,13 +229,44 @@ <a class="modal-close waves-effect waves-green btn-flat">Close</a> </div> </div> +{% endif %} + {% endblock modals %} {% block scripts %} {{ super() }} <script> +{% if current_user == corpus.user or current_user.is_administrator() %} let corpusDisplay = new CorpusDisplay(document.querySelector('#corpus-display')); +{% endif %} + +let corpusFileList = new CorpusFileList(document.querySelector('#corpus-file-list')); +corpusFileList.add( + [ + {% for corpus_file in corpus.files %} + {{ corpus_file.to_json_serializeable()|tojson }}, + {% endfor %} + ] +); + +{% if current_user.is_following_corpus(corpus) %} + {% if cfr.has_permission('MANAGE_FILES') %} + let buildButton = document.querySelector('#build-button'); + buildButton.addEventListener('click', () => { + Requests.corpora.entity.build({{ corpus.hashid|tojson }}) + }); + {% endif %} + + let unfollowRequestElement = document.querySelector('.action-button[data-action="unfollow-request"]'); + unfollowRequestElement.addEventListener('click', () => { + Requests.corpora.entity.followers.entity.delete({{ corpus.hashid|tojson }}, {{ current_user.hashid|tojson }}) + .then((response) => { + window.location.reload(); + }); + }); +{% endif %} +{% if current_user == corpus.user or current_user.is_administrator() %} // #region Publishing let publishingModalIsPublicSwitchElement = document.querySelector('#publishing-modal-is-public-switch'); publishingModalIsPublicSwitchElement.addEventListener('change', (event) => { @@ -245,8 +287,10 @@ deleteModalDeleteButtonElement.addEventListener('click', (event) => { }); }); // #endregion Delete +{% endif %} -// #region Invite users +{% if cfr.has_permission('MANAGE_FOLLOWERS') %} +// #region Invite user let inviteUserModalElement = document.querySelector('#invite-user-modal'); let inviteUserModalSearchElement = document.querySelector('#invite-user-modal-search'); let inviteUserModalInviteButtonElement = document.querySelector('#invite-user-modal-invite-button'); @@ -289,7 +333,7 @@ inviteUserModalInviteButtonElement.addEventListener('click', (event) => { let usernames = inviteUserModalSearch.chipsData.map((chipData) => chipData.tag); Requests.corpora.entity.followers.add({{ corpus.hashid|tojson }}, usernames); }); -// #endregion Invite users +// #endregion Invite user // #region Share link let shareLinkModalElement = document.querySelector('#share-link-modal'); @@ -351,5 +395,8 @@ shareLinkModalOutputCopyButtonElement.addEventListener('click', (event) => { }); // #endregion Share link +{% endif %} + </script> + {% endblock scripts %} diff --git a/app/templates/corpora/public_corpus.html.j2 b/app/templates/corpora/public_corpus.html.j2 deleted file mode 100644 index 31889203..00000000 --- a/app/templates/corpora/public_corpus.html.j2 +++ /dev/null @@ -1,122 +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"> - {% 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 outlined">close</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.analysis', corpus_id=corpus.id) }}">Analyze</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 Owner</span> - <div class="row"> - <div class="col s12"> - <table> - <tr> - <td style="width:10%; margin-top:25px;"> - <img src="{{ url_for('users.user_avatar', user_id=corpus.user.id) }}" alt="user-image" class="circle responsive-img"> - </td> - <td></td> - <td> - <ul> - <li><b>{{ corpus.user.username }}</b></li> - {% if corpus.user.full_name %} - <li>{{ corpus.user.full_name }}</li> - {% endif %} - {% if corpus.user.show_email %} - <li></li><a href="mailto:{{ corpus.user.email }}">{{ corpus.user.email }}</a></li> - {% endif %} - </ul> - </td> - </tr> - </table> - <br> - <p></p> - {% if not current_user.is_following_corpus(corpus) %} - <a class="waves-effect waves-light btn-small">Request Corpus</a> - {% endif %} - <a class="waves-effect waves-light btn-small" href="{{ url_for('users.user', user_id=corpus.user.id) }}">View profile</a> - </div> - </div> - </div> - </div> - - {% if cfa.role.has_permission('VIEW') %} - <div class="card"> - <div class="card-content"> - <span class="card-title" id="corpus-files">Corpus files</span> - <div class="corpus-file-list no-autoinit" id="corpus-file-list"></div> - </div> - </div> - {% endif %} - - {% if cfa.role.has_permission('UPDATE_FOLLOWER') %} - <div class="card"> - <div class="card-content"> - <span class="card-title" id="corpus-followers">Corpus followers</span> - <div class="corpus-follower-list no-autoinit"></div> - </div> - </div> - {% endif %} - - </div> - </div> -</div> -{% endblock page_content %} - - -{% block scripts %} -{{ super() }} -<script> -let publicCorpusFileList = new PublicCorpusFileList(document.querySelector('#corpus-file-list')); -publicCorpusFileList.add( - [ - {% for corpus_file in corpus.files %} - {{ corpus_file.to_json_serializeable()|tojson }}, - {% endfor %} - ] -); - -let unfollowRequestElement = document.querySelector('.action-button[data-action="unfollow-request"]'); -unfollowRequestElement.addEventListener('click', () => { - Requests.corpora.entity.followers.entity.delete({{ corpus.hashid|tojson }}, {{ corpus.user.hashid|tojson }}) - .then((response) => { - window.location.href = {{ url_for('main.dashboard')|tojson }}; - }); -}); -</script> -{% endblock scripts %} -- GitLab