diff --git a/app/corpora/files/json_routes.py b/app/corpora/files/json_routes.py index e4f06084410905356cb48d8197c857ee3a79becd..f8d5ddb4ce45c0475d1fa40ea117264dd6c7684e 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 1df7c9bf26419707c8a4d66965da01f561c923a3..e5ad094d158ca5a40b7e87c6d8d9d278fbebc320 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 f8517359e81bc8d441fd08d2bef4cde8b87b0055..9b31f8eedcfb1601381ddc2a7ccf7cef713236d9 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 e73281ed6efc8efe704ae1882fc302ea9a3ac37b..908f160487aae1c2b12a19c6e973f15adc94ec05 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 75f7dfc0238d3116baba63f371c43bb834084858..8ef108b4e5fc2e033c5363bc75a25517f557c95d 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 8bbfb6655d6e365fa0ee3ed0cb9042dfd436de73..2ee313ec81b50f0218634769ac3c599c16f3c11c 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 0ce96e2dbaf1703c66ecef5860b243961e7e4faf..813676fb4588f6fb14b5b3566654151924d24235 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 826293546d323803dc00c035229c3143d30deaaf..1d0b487792aff117c29c5d37e8c8fe7702f2614b 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 37a284cf117ee285a9d3252f38872e280ceb1a3e..0000000000000000000000000000000000000000 --- 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 f893e898f4dcd9aa055a83369a4c3d2acbcd1eb9..07d2d59a303cd1607d923a728fda3f895c06b84a 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 0000000000000000000000000000000000000000..1620d1773979f62cb632d3c810214e103f380831 --- /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 0000000000000000000000000000000000000000..bbaa2ceea0292513171a175b94e73467da5302e0 --- /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 757e3a546ce46d4fda09d680f96e281d7927756b..cbfd6a6a81cb36d9d00ae6910e31c0f9abeaf9d2 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 20f66b8f8d22cbafce23e975d195a1e0a77c4cf6..02d4245d8828c900c76ec17692b7ffe4a3d3c135 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 31889203deb9e4725a250fc788558a166441445e..0000000000000000000000000000000000000000 --- 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 %}