From 6c76d27a3214607e7ab28de42468965adf461546 Mon Sep 17 00:00:00 2001 From: Inga Kirschnick 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('//files/', 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('//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('//files/', 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('//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('//followers//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('//followers/', 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('//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('//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('//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 ` - - - - - - - delete - file_download - send - - - `.trim(); + return (values) => { + return ` + + + + + + + delete + file_download + send + + + `.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 ` - - - - - - - send - - - `.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 %} +
+ KBuild +
+ + + +{% endset %} + +{% set follower %} + {% if cfr.has_permission('MANAGE_FILES') %} +
+ {% if corpus.status.name in ['UNPREPARED'] %} + KBuild + {% else %} + KBuild + {% endif %} +
+ {% if corpus.status.name in ['BUILT', 'STARTING_ANALYSIS_SESSION', 'RUNNING_ANALYSIS_SESSION', 'CANCELING_ANALYSIS_SESSION'] %} + + {% endif %} + {% endif %} + {% if current_user.is_following_corpus(corpus) %} + + {% 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 %} +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+
+
+
+
+{% endset %} + +{% set public %} +
+
+
+
+
+

Status:

+
+
+
+

Description: {{ corpus.description }}

+
+
+
+

Creation date: {{ corpus.creation_date }}

+
+
+
+

Number of tokens used: {{ corpus.num_tokens }}

+
+
+
+
+
+{% 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 @@
-

-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - -
-
- -
-
- - -
-
- -
-
- - -
-
-
-
-
+

{{ corpus.title }}

+ {% 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') %}
Actions
-
- KBuild -
- - - + {% if current_user == corpus.user or current_user.is_administrator() %} + {{ action_buttons.owner_admin }} + {% else %} + {{ action_buttons.follower }} + {% endif %}
+ {% if cfr.has_permission('MANAGE_FOLLOWERS') %} Social
@@ -79,24 +40,66 @@ linkShare link
+ {% endif %}
+ {% endif %} + + {% if current_user != corpus.user %} +
+
+
+ Corpus Owner +
+
+ + + + + + +
+ user-image + +
    +
  • {{ corpus.user.username }}
  • + {% if corpus.user.full_name %} +
  • {{ corpus.user.full_name }}
  • + {% endif %} + {% if corpus.user.show_email %} +
  • {{ corpus.user.email }} + {% endif %} +
+
+
+

+ {% if not current_user.is_following_corpus(corpus) %} + Request Corpus + {% endif %} + View profile +
+
+
+
+
+ {% endif %}
Corpus files -
+
+ {% if cfr.has_permission('MANAGE_FILES') %} + {% endif %}
-
- + {% if cfr.has_permission('MANAGE_FOLLOWERS') %}
@@ -105,12 +108,16 @@
+ {% endif %} +
{% endblock page_content %} {% block modals %} {{ super() }} + +{% if cfr.has_permission('MANAGE_FOLLOWERS') %} +{% endif %} +{% if current_user == corpus.user or current_user.is_administrator() %} +{% endif %} +{% if cfr.has_permission('MANAGE_FOLLOWERS') %}