diff --git a/app/corpora/routes.py b/app/corpora/routes.py index a81734f8..25c5228f 100644 --- a/app/corpora/routes.py +++ b/app/corpora/routes.py @@ -54,6 +54,7 @@ def corpus(corpus_id): # TODO: Better solution for filtering admin users = User.query.filter(User.is_public == True, User.id != current_user.id, User.id != corpus.user.id, User.role_id < 4).all() cfa = CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id, follower_id=current_user.id).first() + cfas = CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id).all() if cfa is None: if corpus.user == current_user or current_user.is_administrator(): cfr = CorpusFollowerRole.query.filter_by(name='Administrator').first() @@ -61,14 +62,29 @@ def corpus(corpus_id): 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 - ) + if corpus.user == current_user or current_user.is_administrator(): + return render_template( + 'corpora/corpus.html.j2', + title=corpus.title, + corpus=corpus, + cfr=cfr, + cfrs=cfrs, + users = users + ) + if (current_user.is_following_corpus(corpus) or corpus.is_public): + cfas = CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id).all() + return render_template( + 'corpora/public_corpus.html.j2', + title=corpus.title, + corpus=corpus, + cfrs=cfrs, + cfr=cfr, + cfas=cfas, + cfa=cfa, + users = users + ) + abort(403) + @bp.route('//analysis') diff --git a/app/static/js/ResourceLists/CorpusFileList.js b/app/static/js/ResourceLists/CorpusFileList.js index 813676fb..ac87e8e5 100644 --- a/app/static/js/ResourceLists/CorpusFileList.js +++ b/app/static/js/ResourceLists/CorpusFileList.js @@ -8,7 +8,11 @@ class CorpusFileList extends ResourceList { constructor(listContainerElement, options = {}) { super(listContainerElement, options); this.listjs.list.addEventListener('click', (event) => {this.onClick(event)}); + document.querySelectorAll('.selection-action-trigger[data-selection-action]').forEach((element) => { + element.addEventListener('click', (event) => {this.onSelectionAction(event)}); + }); this.isInitialized = false; + this.selectedItemIds = []; this.userId = listContainerElement.dataset.userId; this.corpusId = listContainerElement.dataset.corpusId; this.hasPermissionView = listContainerElement.dataset?.hasPermissionView == 'true' || false; @@ -29,6 +33,12 @@ class CorpusFileList extends ResourceList { return (values) => { return ` + + + @@ -68,11 +78,20 @@ class CorpusFileList extends ResourceList { + - + @@ -97,11 +116,12 @@ class CorpusFileList extends ResourceList { } onClick(event) { + if (event.target.closest('.disable-on-click') !== null) {return;} let listItemElement = event.target.closest('.list-item[data-id]'); if (listItemElement === null) {return;} let itemId = listItemElement.dataset.id; let listActionElement = event.target.closest('.list-action-trigger[data-list-action]'); - let listAction = listActionElement === null ? 'view' : listActionElement.dataset.listAction; + let listAction = listActionElement === null ? '' : listActionElement.dataset.listAction; switch (listAction) { case 'delete': { let values = this.listjs.get('id', itemId)[0].values(); @@ -145,12 +165,156 @@ class CorpusFileList extends ResourceList { window.location.href = `/corpora/${this.corpusId}/files/${itemId}`; break; } + case 'select': { + if (event.target.checked) { + this.selectedItemIds.push(itemId); + } else { + let index = this.selectedItemIds.indexOf(itemId); + if (index > -1) { + this.selectedItemIds.splice(index, 1); + } + } + this.renderingItemSelection(); + } default: { break; } } } + onSelectionAction(event) { + let selectionActionElement = event.target.closest('.selection-action-trigger[data-selection-action]'); + let selectionAction = selectionActionElement.dataset.selectionAction; + let items = this.listjs.items; + let selectableItems = Array.from(items) + .filter(item => item.elm) + .map(item => item.elm.querySelector('input[type="checkbox"]')); + + switch (selectionAction) { + case 'select-all': { + let selectedIds = Array.from(items) + .map(item => item.values().id); + if (event.target.checked) { + selectableItems.forEach(selectableItem => selectableItem.checked = true); + this.selectedItemIds = selectedIds; + } else { + selectableItems.forEach(checkbox => checkbox.checked = false); + this.selectedItemIds = this.selectedItemIds.filter(id => !selectedIds.includes(id)); + } + this.renderingItemSelection(); + break; + } + case 'delete': { + let modalElement = Utils.HTMLToElement( + ` + + ` + ); + document.querySelector('#modals').appendChild(modalElement); + let itemList = document.querySelector('#selected-items-list'); + this.selectedItemIds.forEach(selectedItemId => { + let listItem = this.listjs.get('id', selectedItemId)[0].elm; + let values = this.listjs.get('id', listItem.dataset.id)[0].values(); + let itemElement = Utils.HTMLToElement(`
  • - ${values.title}
  • `); + itemList.appendChild(itemElement); + }); + let modal = M.Modal.init( + modalElement, + { + dismissible: false, + onCloseEnd: () => { + modal.destroy(); + modalElement.remove(); + } + } + ); + let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]'); + confirmElement.addEventListener('click', (event) => { + this.selectedItemIds.forEach(selectedItemId => { + Requests.corpora.entity.files.ent.delete(this.corpusId, selectedItemId); + }); + this.selectedItemIds = []; + this.renderingItemSelection(); + }); + modal.open(); + break; + } + case 'download': { + this.selectedItemIds.forEach(selectedItemId => { + let downloadLink = document.createElement('a'); + downloadLink.href = `/corpora/${this.corpusId}/files/${selectedItemId}/download`; + downloadLink.download = ''; + downloadLink.click(); + }); + selectableItems.forEach(checkbox => checkbox.checked = false); + this.selectedItemIds = []; + this.renderingItemSelection(); + break; + } + default: { + break; + } + } + } + + renderingItemSelection() { + let selectionActionButtons; + if (this.hasPermissionManageFiles) { + selectionActionButtons = document.querySelectorAll('.selection-action-trigger:not([data-selection-action="select-all"])'); + } else if (this.hasPermissionView) { + selectionActionButtons = document.querySelectorAll('.selection-action-trigger:not([data-selection-action="select-all"]):not([data-selection-action="delete"])'); + } + let selectableItems = this.listjs.items; + let actionButtons = []; + + Object.values(selectableItems).forEach(selectableItem => { + if (selectableItem.elm) { + let checkbox = selectableItem.elm.querySelector('input[type="checkbox"]'); + if (checkbox.checked) { + selectableItem.elm.classList.add('grey', 'lighten-3'); + } else { + selectableItem.elm.classList.remove('grey', 'lighten-3'); + } + let itemActionButtons = []; + if (this.hasPermissionManageFiles) { + itemActionButtons = selectableItem.elm.querySelectorAll('.list-action-trigger:not([data-list-action="select"])'); + } else if (this.hasPermissionView) { + itemActionButtons = selectableItem.elm.querySelectorAll('.list-action-trigger:not([data-list-action="select"]):not([data-list-action="delete"]):not([data-list-action="view"])'); + } + itemActionButtons.forEach(itemActionButton => { + actionButtons.push(itemActionButton); + }); + } + }); + // Hide item action buttons if > 0 item is selected and show selection action buttons + if (this.selectedItemIds.length > 0) { + selectionActionButtons.forEach(selectionActionButton => { + selectionActionButton.classList.remove('hide'); + }); + actionButtons.forEach(actionButton => { + actionButton.classList.add('hide'); + }); + } else { + selectionActionButtons.forEach(selectionActionButton => { + selectionActionButton.classList.add('hide'); + }); + actionButtons.forEach(actionButton => { + actionButton.classList.remove('hide'); + }); + } + } + onPatch(patch) { let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)`); let filteredPatch = patch.filter(operation => re.test(operation.path)); diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2 index 07d2d59a..bee5b7b2 100644 --- a/app/templates/_scripts.html.j2 +++ b/app/templates/_scripts.html.j2 @@ -40,7 +40,6 @@ 'js/ResourceLists/ResourceList.js', 'js/ResourceLists/CorpusFileList.js', 'js/ResourceLists/CorpusList.js', - 'js/ResourceLists/FollowedCorpusList.js', 'js/ResourceLists/PublicCorpusList.js', 'js/ResourceLists/JobList.js', 'js/ResourceLists/JobInputList.js', diff --git a/app/templates/corpora/public_corpus.html.j2 b/app/templates/corpora/public_corpus.html.j2 new file mode 100644 index 00000000..fb13c252 --- /dev/null +++ b/app/templates/corpora/public_corpus.html.j2 @@ -0,0 +1,379 @@ +{% 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 %} +
    +
    +
    +

    {{ corpus.title }}

    +
    +
    +
    +
    +

    +
    + +
    +
    + + +
    +
    + +
    +
    + + +
    +
    + +
    +
    + + +
    +
    +
    +
    +
    +
    + + {% if cfr.has_permission('VIEW') %} +
    +
    +
    + Actions +
    + {% if cfr.has_permission('MANAGE_FILES') %} +
    + KBuild +
    +
    + {% if corpus.status.name in ['BUILT', 'STARTING_ANALYSIS_SESSION', 'RUNNING_ANALYSIS_SESSION', 'CANCELING_ANALYSIS_SESSION'] and current_user.is_following_corpus(corpus) %} + searchAnalyze + {% else %} + searchAnalyze + {% endif %} +
    + {% endif %} + {% if current_user.is_following_corpus(corpus) %} + + {% endif %} +
    + {% if cfr.has_permission('MANAGE_FOLLOWERS') %} + Social + + {% endif %} +
    +
    +
    + {% endif %} + +
    +
    +
    + Corpus Owner +
    +
    +
    + + Filename Author Title Publishing year + delete + file_download +
    + + + + + +
    + 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) and corpus.user.has_profile_privacy_setting('SHOW_EMAIL') %} + Request Corpus + {% endif %} + View profile + + + + + + +
    +
    +
    + Corpus files +
    +
    + {% if cfr.has_permission('MANAGE_FILES') %} + + {% endif %} +
    +
    + + {% if cfr.has_permission('MANAGE_FOLLOWERS') %} +
    +
    +
    + Corpus followers +
    +
    +
    +
    + {% endif %} + + + +{% endblock page_content %} + +{% block modals %} +{{ super() }} + +{% if cfr.has_permission('MANAGE_FOLLOWERS') %} + + + +{% endif %} + +{% endblock modals %} + +{% block scripts %} +{{ super() }} + +{% endblock scripts %}