CorpusFile selection+restore public_corpus page

This commit is contained in:
Inga Kirschnick
2023-05-12 13:43:38 +02:00
parent 1c47d2a346
commit 0cf955bd2f
4 changed files with 569 additions and 11 deletions

View File

@ -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',

View File

@ -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 %}
<div class="container">
<div class="row">
<div class="col s12">
<h1>{{ corpus.title }}</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-text corpus-status-color white-text" data-status="{{ corpus.status.name }}"></span></p>
<div class="row">
<div class="col s12">
<div class="input-field">
<label>Description</label>
<input disabled type="text" value="{{ corpus.description }}">
</div>
</div>
<div class="col s12 m6">
<div class="input-field">
<label for="corpus-creation-date">Creation date</label>
<input disabled type="text" value="{{ corpus.creation_date }}">
</div>
</div>
<div class="col s12 m6">
<div class="input-field">
<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>
<input disabled type="text" value="{{ corpus.num_tokens }}">
</div>
</div>
</div>
</div>
</div>
</div>
{% 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">
{% if cfr.has_permission('MANAGE_FILES') %}
<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;">
{% if corpus.status.name in ['BUILT', 'STARTING_ANALYSIS_SESSION', 'RUNNING_ANALYSIS_SESSION', 'CANCELING_ANALYSIS_SESSION'] and current_user.is_following_corpus(corpus) %}
<a class="action-button btn 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>
{% else %}
<a class="action-button btn disabled waves-effect waves-light" data-action="analyze" style="width: 100%;"><i class="material-icons left">search</i>Analyze</a>
{% endif %}
</div>
{% 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 %}
</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;">
<a class="btn waves-effect waves-light modal-trigger" href="#invite-user-modal" style="width: 100%;"><i class="material-icons left">person_add</i>invite user</a>
</div>
<div class="col s12 l6" style="padding: 0 2.5px;">
<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 %}
<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) and corpus.user.has_profile_privacy_setting('SHOW_EMAIL') %}
<a class="waves-effect waves-light btn-small" href="mailto:{{ corpus.user.email }}">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>
<div class="col s12">
<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" 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>
{% if cfr.has_permission('MANAGE_FOLLOWERS') %}
<div class="col s12">
<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>
</div>
{% endif %}
</div>
</div>
{% endblock page_content %}
{% block modals %}
{{ super() }}
{% 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>
<p>
Add other nopaque users as followers to your corpus. You can also add multiple
users at the same time. Added users get the role of "viewer"
by default, so they are only allowed to analyze files within nopaque, but not
to download or edit them. You can customize the roles later below.
</p>
<p><b>Please make sure that the invited users are legally allowed to view the included corpus files.</b></p>
<div class="row">
<div class="col s10">
<div class="chips no-autoinit" id="invite-user-modal-search"></div>
</div>
<div class="col s2">
<br class="hide-on-med-and-down">
<a class="btn modal-close waves-effect waves-light" id="invite-user-modal-invite-button">Invite<i class="material-icons right">send</i></a>
</div>
</div>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect waves-green btn-flat">Close</a>
</div>
</div>
<div class="modal no-autoinit" id="share-link-modal">
<div class="modal-content">
<h4>Create a link to share your corpus</h4>
<p>
With the link other users follow your corpus directly, if it has not expired.
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>
<p><b>Please make sure that the invited users are legally allowed to view the included corpus files.</b></p>
<div class="row">
<div class="col s12 l2">
<div class="input-field">
<i class="material-icons prefix">badge</i>
<select id="share-link-modal-corpus-follower-role-select">
{% for cfr in cfrs %}
<option value="{{ cfr.name }}">{{ cfr.name }}</option>
{% endfor %}
</select>
<label>Role</label>
</div>
</div>
<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">
<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>
<div class="col s12 l5">
<div class="row hide" id="share-link-modal-output-container">
<div class="col s9">
<div class="input-field">
<input disabled id="share-link-modal-output-field" readonly type="text">
</div>
</div>
<div class="col s3">
<br class="hide-on-med-and-down">
<a class="btn-small waves-effect waves-light" id="share-link-modal-output-copy-button"><i class="material-icons left">content_copy</i>Copy</a>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect waves-green btn-flat">Close</a>
</div>
</div>
{% endif %}
{% endblock modals %}
{% block scripts %}
{{ super() }}
<script>
let publicCorpusFileList = new CorpusFileList(document.querySelector('#corpus-file-list'));
publicCorpusFileList.add(
[
{% for corpus_file in corpus.files %}
{{ corpus_file.to_json_serializeable()|tojson }},
{% endfor %}
]
);
{% if cfr.has_permission('MANAGE_FOLLOWERS') %}
let publicCorpusFollowerList = new CorpusFollowerList(document.querySelector('.corpus-follower-list'));
publicCorpusFollowerList.add(
[
{% for cfa in cfas %}
{{ cfa.to_json_serializeable()|tojson }},
{% endfor %}
]
);
{% endif %}
// #region Corpus Unfollow Request
{% if current_user.is_following_corpus(corpus) %}
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 %}
// #endregion Corpus Unfollow Request
{% 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');
let users = {
{% for user in users %}
{{ user.username|tojson }}: {{ url_for('users.user_avatar', user_id=user.id)|tojson }}
{% if not loop.last %},{% endif %}
{% endfor %}
};
let inviteUserModalSearch = M.Chips.init(
inviteUserModalSearchElement,
{
autocompleteOptions: {
data: users
},
limit: 3,
onChipAdd: (a, chipElement) => {
if (!(chipElement.firstChild.data in inviteUserModalSearch.autocomplete.options.data)) {
chipElement.firstElementChild.click();
}
},
placeholder: 'Enter username',
secondaryPlaceholder: 'Add more users'
}
);
M.Modal.init(
inviteUserModalElement,
{
onOpenStart: (modalElement, modalTriggerElement) => {
while (inviteUserModalSearch.chipsData.length > 0) {
inviteUserModalSearch.deleteChip(0);
}
}
}
)
inviteUserModalInviteButtonElement.addEventListener('click', (event) => {
let usernames = inviteUserModalSearch.chipsData.map((chipData) => chipData.tag);
Requests.corpora.entity.followers.add({{ corpus.hashid|tojson }}, usernames);
});
// #endregion Invite user
// #region Share link
let shareLinkModalElement = document.querySelector('#share-link-modal');
let shareLinkModalCorpusFollowerRoleSelectElement = document.querySelector('#share-link-modal-corpus-follower-role-select');
let shareLinkModalExpirationDateDatepickerElement = document.querySelector('#share-link-modal-expiration-date-datepicker');
let shareLinkModalCreateButtonElement = document.querySelector('#share-link-modal-create-button');
let shareLinkModalOutputContainerElement = document.querySelector('#share-link-modal-output-container');
let shareLinkModalOutputFieldElement = document.querySelector('#share-link-modal-output-field');
let shareLinkModalOutputCopyButtonElement = document.querySelector('#share-link-modal-output-copy-button');
let today = new Date();
let tomorrow = new Date();
tomorrow.setDate(today.getDate() + 1);
let oneWeekLater = new Date();
oneWeekLater.setDate(today.getDate() + 7);
let fourWeeksLater = new Date();
fourWeeksLater.setDate(today.getDate() + 28);
M.Datepicker.init(
shareLinkModalExpirationDateDatepickerElement,
{
container: document.querySelector('main'),
defaultDate: oneWeekLater,
setDefaultDate: true,
minDate: tomorrow,
maxDate: fourWeeksLater
}
);
M.Modal.init(
shareLinkModalElement,
{
onOpenStart: (modalElement, modalTriggerElement) => {
shareLinkModalOutputFieldElement.value = '';
shareLinkModalOutputContainerElement.classList.add('hide');
}
}
)
shareLinkModalCreateButtonElement.addEventListener('click', (event) => {
let role = shareLinkModalCorpusFollowerRoleSelectElement.value;
let expiration = shareLinkModalExpirationDateDatepickerElement.value
Requests.corpora.entity.generateShareLink({{ corpus.hashid|tojson }}, role, expiration)
.then((response) => {
response.json()
.then((json) => {
shareLinkModalOutputContainerElement.classList.remove('hide');
shareLinkModalOutputFieldElement.value = json.corpusShareLink;
});
});
});
shareLinkModalOutputCopyButtonElement.addEventListener('click', (event) => {
navigator.clipboard.writeText(shareLinkModalOutputFieldElement.value)
.then(
() => {app.flash('Copied!');},
() => {app.flash('Could not copy to clipboard. Please copy manually.', 'error');}
);
});
// #endregion Share link
{% endif %}
</script>
{% endblock scripts %}