mirror of
				https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
				synced 2025-11-03 20:02:47 +00:00 
			
		
		
		
	Merge branch 'following-mechanics' into development
This commit is contained in:
		@@ -1,6 +1,7 @@
 | 
			
		||||
from flask_wtf import FlaskForm
 | 
			
		||||
from flask_wtf.file import FileField, FileRequired
 | 
			
		||||
from wtforms import (
 | 
			
		||||
    BooleanField,
 | 
			
		||||
    StringField,
 | 
			
		||||
    SubmitField,
 | 
			
		||||
    TextAreaField,
 | 
			
		||||
@@ -77,6 +78,9 @@ class UpdateCorpusFileForm(CorpusFileBaseForm):
 | 
			
		||||
            kwargs['prefix'] = 'update-corpus-file-form'
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
class ChangeCorpusSettingsForm(FlaskForm):
 | 
			
		||||
    is_public = BooleanField('Public Corpus')
 | 
			
		||||
    submit = SubmitField()
 | 
			
		||||
 | 
			
		||||
class ImportCorpusForm(FlaskForm):
 | 
			
		||||
    pass
 | 
			
		||||
 
 | 
			
		||||
@@ -5,15 +5,17 @@ from flask import (
 | 
			
		||||
    Markup,
 | 
			
		||||
    redirect,
 | 
			
		||||
    render_template,
 | 
			
		||||
    send_from_directory
 | 
			
		||||
    request,
 | 
			
		||||
    send_from_directory,
 | 
			
		||||
    url_for
 | 
			
		||||
)
 | 
			
		||||
from flask_login import current_user, login_required
 | 
			
		||||
from threading import Thread
 | 
			
		||||
import os
 | 
			
		||||
from app import db
 | 
			
		||||
from app.models import Corpus, CorpusFile, CorpusStatus
 | 
			
		||||
from app import db, hashids
 | 
			
		||||
from app.models import Corpus, CorpusFile, CorpusStatus, CorpusFollowerAssociation, User
 | 
			
		||||
from . import bp
 | 
			
		||||
from .forms import CreateCorpusFileForm, CreateCorpusForm, UpdateCorpusFileForm
 | 
			
		||||
from .forms import ChangeCorpusSettingsForm, CreateCorpusFileForm, CreateCorpusForm, UpdateCorpusFileForm
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def user_can_read_corpus(user, corpus):
 | 
			
		||||
@@ -64,19 +66,35 @@ def create_corpus():
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>')
 | 
			
		||||
@bp.route('/<hashid:corpus_id>', methods=['GET', 'POST'])
 | 
			
		||||
@login_required
 | 
			
		||||
def corpus(corpus_id):
 | 
			
		||||
    corpus = Corpus.query.get_or_404(corpus_id)
 | 
			
		||||
    if not user_can_read_corpus(current_user, corpus):
 | 
			
		||||
        abort(403)
 | 
			
		||||
    corpus_settings_form = ChangeCorpusSettingsForm(
 | 
			
		||||
        data=corpus.to_json_serializeable(),
 | 
			
		||||
        prefix='corpus-settings-form'
 | 
			
		||||
    )
 | 
			
		||||
    if corpus_settings_form.validate_on_submit():
 | 
			
		||||
        corpus.is_public = corpus_settings_form.is_public.data
 | 
			
		||||
        db.session.commit()
 | 
			
		||||
        flash('Your changes have been saved')
 | 
			
		||||
        return redirect(url_for('.corpus', corpus_id=corpus.id))
 | 
			
		||||
    # following_users = [
 | 
			
		||||
    #     u.to_json_serializeable() for u
 | 
			
		||||
    #     in corpus.following_users
 | 
			
		||||
    # ]
 | 
			
		||||
    return render_template(
 | 
			
		||||
        'corpora/corpus.html.j2',
 | 
			
		||||
        corpus_settings_form=corpus_settings_form,
 | 
			
		||||
        corpus=corpus,
 | 
			
		||||
        # following_users=following_users,
 | 
			
		||||
        title='Corpus'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# @bp.route('/<hashid:corpus_id>/update')
 | 
			
		||||
# @login_required
 | 
			
		||||
# def update_corpus(corpus_id):
 | 
			
		||||
@@ -263,3 +281,54 @@ def import_corpus():
 | 
			
		||||
@login_required
 | 
			
		||||
def export_corpus(corpus_id):
 | 
			
		||||
    abort(503)
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/follow', methods=['GET', 'POST'])
 | 
			
		||||
@login_required
 | 
			
		||||
def follow_corpus(corpus_id):
 | 
			
		||||
    corpus = Corpus.query.get_or_404(corpus_id)
 | 
			
		||||
    user_hashid = request.args.get('user_id')
 | 
			
		||||
    if user_hashid is None:
 | 
			
		||||
        user = current_user
 | 
			
		||||
    else:
 | 
			
		||||
        if not current_user.is_administrator():
 | 
			
		||||
            abort(403)
 | 
			
		||||
        else:
 | 
			
		||||
            user_id = hashids.decode(user_hashid)
 | 
			
		||||
            user = User.query.get_or_404(user_id)
 | 
			
		||||
    if not user.is_following_corpus(corpus):
 | 
			
		||||
        user.follow_corpus(corpus)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return {}, 202
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/unfollow', methods=['GET', 'POST'])
 | 
			
		||||
@login_required
 | 
			
		||||
def unfollow_corpus(corpus_id):
 | 
			
		||||
    corpus = Corpus.query.get_or_404(corpus_id)
 | 
			
		||||
    user_hashid = request.args.get('user_id')
 | 
			
		||||
    if user_hashid is None:
 | 
			
		||||
        user = current_user
 | 
			
		||||
    else:
 | 
			
		||||
        if not current_user.is_administrator():
 | 
			
		||||
            abort(403)
 | 
			
		||||
        else:
 | 
			
		||||
            user_id = hashids.decode(user_hashid)
 | 
			
		||||
            user = User.query.get_or_404(user_id)
 | 
			
		||||
    if user.is_following_corpus(corpus):
 | 
			
		||||
        user.unfollow_corpus(corpus)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return {}, 202
 | 
			
		||||
 | 
			
		||||
@bp.route('/add_permission/<hashid:corpus_id>/<hashid:user_id>/<int:permission>')
 | 
			
		||||
def add_permission(corpus_id, user_id, permission):
 | 
			
		||||
    a = CorpusFollowerAssociation.query.filter_by(followed_corpus_id=corpus_id, following_user_id=user_id).first_or_404()
 | 
			
		||||
    a.add_permission(permission)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return 'ok'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('/remove_permission/<hashid:corpus_id>/<hashid:user_id>/<int:permission>')
 | 
			
		||||
def remove_permission(corpus_id, user_id, permission):
 | 
			
		||||
    a = CorpusFollowerAssociation.query.filter_by(followed_corpus_id=corpus_id, following_user_id=user_id).first_or_404()
 | 
			
		||||
    a.remove_permission(permission)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return 'ok'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
from flask import flash, redirect, render_template, url_for
 | 
			
		||||
from flask_login import current_user, login_required, login_user
 | 
			
		||||
from app.auth.forms import LoginForm
 | 
			
		||||
from app.models import User
 | 
			
		||||
from app.models import Corpus, User
 | 
			
		||||
from . import bp
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -31,7 +31,11 @@ def dashboard():
 | 
			
		||||
        u.to_json_serializeable(filter_by_privacy_settings=True) for u
 | 
			
		||||
        in User.query.filter(User.is_public == True, User.id != current_user.id).all()
 | 
			
		||||
    ]
 | 
			
		||||
    return render_template('main/dashboard.html.j2', title='Dashboard', users=users)
 | 
			
		||||
    corpora = [
 | 
			
		||||
        c.to_json_serializeable() for c
 | 
			
		||||
        in Corpus.query.filter(Corpus.is_public == True).all()
 | 
			
		||||
    ]
 | 
			
		||||
    return render_template('main/dashboard.html.j2', title='Dashboard', users=users, corpora=corpora)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('/dashboard2')
 | 
			
		||||
 
 | 
			
		||||
@@ -68,6 +68,11 @@ class ProfilePrivacySettings(IntEnum):
 | 
			
		||||
    SHOW_EMAIL = 1
 | 
			
		||||
    SHOW_LAST_SEEN = 2
 | 
			
		||||
    SHOW_MEMBER_SINCE = 4
 | 
			
		||||
 | 
			
		||||
class CorpusFollowPermission(IntEnum):
 | 
			
		||||
    VIEW = 1
 | 
			
		||||
    CONTRIBUTE = 2
 | 
			
		||||
    ADMINISTRATE = 4
 | 
			
		||||
# endregion enums
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -298,6 +303,16 @@ class CorpusFollowerAssociation(db.Model):
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return f'<CorpusFollowerAssociation {self.following_user.__repr__()} ~ {self.followed_corpus.__repr__()}>'
 | 
			
		||||
 | 
			
		||||
    def has_permission(self, permission):
 | 
			
		||||
        return self.permissions & permission == permission
 | 
			
		||||
    
 | 
			
		||||
    def add_permission(self, permission):
 | 
			
		||||
        if not self.has_permission(permission):
 | 
			
		||||
            self.permissions += permission
 | 
			
		||||
    
 | 
			
		||||
    def remove_permission(self, permission):
 | 
			
		||||
        if self.has_permission(permission):
 | 
			
		||||
            self.permissions -= permission
 | 
			
		||||
 | 
			
		||||
class User(HashidMixin, UserMixin, db.Model):
 | 
			
		||||
    __tablename__ = 'users'
 | 
			
		||||
@@ -576,6 +591,18 @@ class User(HashidMixin, UserMixin, db.Model):
 | 
			
		||||
        self.profile_privacy_settings = 0
 | 
			
		||||
    #endregion Profile Privacy settings
 | 
			
		||||
 | 
			
		||||
    def follow_corpus(self, corpus):
 | 
			
		||||
        if not self.is_following_corpus(corpus):
 | 
			
		||||
            self.followed_corpora.append(corpus)
 | 
			
		||||
 | 
			
		||||
    def unfollow_corpus(self, corpus):
 | 
			
		||||
        if self.is_following_corpus(corpus):
 | 
			
		||||
            self.followed_corpora.remove(corpus)
 | 
			
		||||
 | 
			
		||||
    def is_following_corpus(self, corpus):
 | 
			
		||||
        return corpus in self.followed_corpora
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def to_json_serializeable(self, backrefs=False, relationships=False, filter_by_privacy_settings=False):
 | 
			
		||||
        json_serializeable = {
 | 
			
		||||
            'id': self.hashid,
 | 
			
		||||
@@ -623,6 +650,10 @@ class User(HashidMixin, UserMixin, db.Model):
 | 
			
		||||
                x.hashid: x.to_json_serializeable(relationships=True)
 | 
			
		||||
                for x in self.spacy_nlp_pipeline_models
 | 
			
		||||
            }
 | 
			
		||||
            json_serializeable['followed_corpora'] = {
 | 
			
		||||
                x.hashid: x.to_json_serializeable(relationships=True)
 | 
			
		||||
                for x in self.followed_corpora
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        if filter_by_privacy_settings:
 | 
			
		||||
            if not self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_EMAIL):
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ class CorpusFileList extends ResourceList {
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    app.getUser(this.userId).then((user) => {
 | 
			
		||||
      this.add(Object.values(user.corpora[this.corpusId].files));
 | 
			
		||||
      this.add(Object.values(user.corpora[this.corpusId].files || user.followed_corpora[this.corpusId].files));
 | 
			
		||||
      this.isInitialized = true;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
class UserList extends ResourceList {
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    for (let publicUserListElement of document.querySelectorAll('.user-list:not(.no-autoinit)')) {
 | 
			
		||||
      new UserList(publicUserListElement);
 | 
			
		||||
    for (let userListElement of document.querySelectorAll('.user-list:not(.no-autoinit)')) {
 | 
			
		||||
      new UserList(userListElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -41,14 +41,14 @@ class UserList extends ResourceList {
 | 
			
		||||
 | 
			
		||||
  initListContainerElement() {
 | 
			
		||||
    if (!this.listContainerElement.hasAttribute('id')) {
 | 
			
		||||
      this.listContainerElement.id = Utils.generateElementId('public-user-list-');
 | 
			
		||||
      this.listContainerElement.id = Utils.generateElementId('user-list-');
 | 
			
		||||
    }
 | 
			
		||||
    let listSearchElementId = Utils.generateElementId(`${this.listContainerElement.id}-search-`);
 | 
			
		||||
    this.listContainerElement.innerHTML = `
 | 
			
		||||
      <div class="input-field">
 | 
			
		||||
        <i class="material-icons prefix">search</i>
 | 
			
		||||
        <input id="${listSearchElementId}" class="search" type="text"></input>
 | 
			
		||||
        <label for="${listSearchElementId}">Search public user</label>
 | 
			
		||||
        <label for="${listSearchElementId}">Search user</label>
 | 
			
		||||
      </div>
 | 
			
		||||
      <table>
 | 
			
		||||
        <thead>
 | 
			
		||||
@@ -77,7 +77,7 @@ class UserList extends ResourceList {
 | 
			
		||||
      'full-name': user.full_name ? user.full_name : '',
 | 
			
		||||
      'location': user.location ? user.location : '',
 | 
			
		||||
      'organization': user.organization ? user.organization : '',
 | 
			
		||||
      'corpora-online': '0'
 | 
			
		||||
      'corpora-online': '-'
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
  <li><a href="{{ url_for('main.dashboard') }}"><i class="material-icons">dashboard</i>Dashboard</a></li>
 | 
			
		||||
  <li><a href="{{ url_for('main.dashboard', _anchor='corpora') }}" style="padding-left: 47px;"><i class="nopaque-icons">I</i>My Corpora</a></li>
 | 
			
		||||
  <li><a href="{{ url_for('main.dashboard', _anchor='jobs') }}" style="padding-left: 47px;"><i class="nopaque-icons">J</i>My Jobs</a></li>
 | 
			
		||||
  <li><a href="{{ url_for('main.dashboard', _anchor='social') }}" style="padding-left: 47px;"><i class="material-icons">groups</i>Social</a></li>
 | 
			
		||||
  <li><a href="{{ url_for('contributions.contributions') }}"><i class="material-icons">new_label</i>Contribute</a></li>
 | 
			
		||||
  <li><div class="divider"></div></li>
 | 
			
		||||
  <li><a class="subheader">Processes & Services</a></li>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
{% extends "base.html.j2" %}
 | 
			
		||||
{% import "materialize/wtf.html.j2" as wtf %}
 | 
			
		||||
{% from "corpora/_breadcrumbs.html.j2" import breadcrumbs with context %}
 | 
			
		||||
 | 
			
		||||
{% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %}
 | 
			
		||||
@@ -9,7 +10,15 @@
 | 
			
		||||
    <div class="col s12" data-corpus-id="{{ corpus.hashid }}" data-user-id="{{ corpus.user.hashid }}" id="corpus-display">
 | 
			
		||||
      <div class="row">
 | 
			
		||||
        <div class="col s8 m9 l10">
 | 
			
		||||
          <h1 id="title"><span class="corpus-title"></span></h1>
 | 
			
		||||
          {# <h1 id="title"><span class="corpus-title"></span></h1> #}
 | 
			
		||||
          <h1 id="title">{{ corpus.title }}</h1>
 | 
			
		||||
          {% if not corpus.user == current_user %}
 | 
			
		||||
          {% if current_user.is_following_corpus(corpus) %}
 | 
			
		||||
          <a class="btn waves-effect waves-light" id="follow-corpus-request"><i class="material-icons left">add</i>Unfollow Corpus</a>
 | 
			
		||||
          {% elif not current_user.is_following_corpus(corpus) %}
 | 
			
		||||
          <a class="btn waves-effect waves-light" id="follow-corpus-request"><i class="material-icons left">add</i>Follow Corpus</a>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
          {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="col s4 m3 l2 right-align">
 | 
			
		||||
          <p> </p>
 | 
			
		||||
@@ -76,6 +85,33 @@
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    {% if current_user.can(Permission.ADMINISTRATE) or current_user.hashid == corpus.user.hashid %}
 | 
			
		||||
    <div class="col s12">
 | 
			
		||||
      <form method="POST">
 | 
			
		||||
      {{ corpus_settings_form.hidden_tag() }}
 | 
			
		||||
        <div class="card">
 | 
			
		||||
          <div class="card-content">
 | 
			
		||||
            <span class="card-title" id="files">Corpus settings</span>
 | 
			
		||||
            <br>
 | 
			
		||||
            <p></p>
 | 
			
		||||
            {{ wtf.render_field(corpus_settings_form.is_public) }}
 | 
			
		||||
            <br>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="card-action right-align">
 | 
			
		||||
            {{ wtf.render_field(corpus_settings_form.submit, material_icon='send') }}
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </form>
 | 
			
		||||
    </div>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    <div class="col s12">
 | 
			
		||||
      <div class="card">
 | 
			
		||||
        <div class="card-content">
 | 
			
		||||
          <span class="card-title" id="files">Corpus followers</span>
 | 
			
		||||
          <div class="user-list no-autoinit"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
{% endblock page_content %}
 | 
			
		||||
@@ -84,5 +120,56 @@
 | 
			
		||||
{{ super() }}
 | 
			
		||||
<script>
 | 
			
		||||
  let corpusDisplay = new CorpusDisplay(document.querySelector('#corpus-display'));
 | 
			
		||||
  let corpusFollowingRequest = document.querySelector('#follow-corpus-request');
 | 
			
		||||
  
 | 
			
		||||
  {# let followingUserList = new UserList(document.querySelector('.user-list'));
 | 
			
		||||
  followingUserList.add({{ following_users|tojson }}); #}
 | 
			
		||||
  
 | 
			
		||||
  corpusFollowingRequest.addEventListener('click', function() {
 | 
			
		||||
    corpusFollowingRequest.innerHTML = '<i class="material-icons left">add</i>Unfollow Corpus';
 | 
			
		||||
    if ("{{ current_user.is_following_corpus(corpus) }}" === "False") {
 | 
			
		||||
      corpusFollowingRequest.lastChild.textContent = 'Unfollow Corpus';
 | 
			
		||||
      return new Promise((resolve, reject) => {
 | 
			
		||||
        fetch(`/corpora/{{ corpus.hashid }}/follow`, {method: 'POST', headers: {Accept: 'application/json'}})
 | 
			
		||||
          .then(
 | 
			
		||||
            (response) => {
 | 
			
		||||
              if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);}
 | 
			
		||||
              if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);}
 | 
			
		||||
              if (response.status === 409) {app.flash('Conflict', 'error'); reject(response);}
 | 
			
		||||
              app.flash(`You follow "{{ corpus.title }}" now`, 'corpus');
 | 
			
		||||
              window.location.href = '{{ url_for("corpora.corpus", corpus_id=corpus.id) }}'
 | 
			
		||||
              resolve(response);
 | 
			
		||||
            },
 | 
			
		||||
            (response) => {
 | 
			
		||||
              app.flash('Something went wrong', 'error');
 | 
			
		||||
              reject(response);
 | 
			
		||||
            }
 | 
			
		||||
          );
 | 
			
		||||
      });
 | 
			
		||||
      
 | 
			
		||||
    } else {
 | 
			
		||||
      corpusFollowingRequest.innerHTML = '<i class="material-icons left">add</i>Unfollow Corpus';
 | 
			
		||||
      return new Promise((resolve, reject) => {
 | 
			
		||||
        fetch(`/corpora/{{ corpus.hashid }}/unfollow`, {method: 'POST', headers: {Accept: 'application/json'}})
 | 
			
		||||
          .then(
 | 
			
		||||
            (response) => {
 | 
			
		||||
              if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);}
 | 
			
		||||
              if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);}
 | 
			
		||||
              if (response.status === 409) {app.flash('Conflict', 'error'); reject(response);}
 | 
			
		||||
              app.flash(`You are not following "{{ corpus.title }}" anymore`, 'corpus');
 | 
			
		||||
              resolve(response);
 | 
			
		||||
              window.location.href = '{{ url_for("corpora.corpus", corpus_id=corpus.id) }}'
 | 
			
		||||
            },
 | 
			
		||||
            (response) => {
 | 
			
		||||
              app.flash('Something went wrong', 'error');
 | 
			
		||||
              reject(response);
 | 
			
		||||
            }
 | 
			
		||||
          );
 | 
			
		||||
      });
 | 
			
		||||
      
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
</script>
 | 
			
		||||
{% endblock scripts %}
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,23 @@
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="col s12" id="social">
 | 
			
		||||
      <h3>Social</h3>
 | 
			
		||||
      <div class="card">
 | 
			
		||||
        <div class="card-content">
 | 
			
		||||
          <span class="card-title">Other users</span>
 | 
			
		||||
          <p>Find other users and follow them to see their corpora.</p>
 | 
			
		||||
          <div class="user-list no-autoinit"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="card">
 | 
			
		||||
        <div class="card-content">
 | 
			
		||||
          <span class="card-title">Public corpora</span>
 | 
			
		||||
          <p>Find public corpora</p>  
 | 
			
		||||
          <div class="public-corpus-list no-autoinit"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
{% endblock page_content %}
 | 
			
		||||
@@ -96,3 +113,14 @@
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
{% endblock modals %}
 | 
			
		||||
 | 
			
		||||
{% block scripts %}
 | 
			
		||||
{{ super() }}
 | 
			
		||||
<script>
 | 
			
		||||
  let userList = new UserList(document.querySelector('.user-list'));
 | 
			
		||||
  userList.add({{ users|tojson }});
 | 
			
		||||
  let publicCorpusList = new CorpusList(document.querySelector('.public-corpus-list'));
 | 
			
		||||
  publicCorpusList.add({{ corpora|tojson }});
 | 
			
		||||
</script>
 | 
			
		||||
{% endblock scripts %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -89,6 +89,24 @@
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="row">
 | 
			
		||||
      <div class="col s6">
 | 
			
		||||
        <div class="card">
 | 
			
		||||
          <div class="card-content">
 | 
			
		||||
            <h4>Groups</h4>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col s6">
 | 
			
		||||
        <div class="card">
 | 
			
		||||
          <div class="card-content">
 | 
			
		||||
            <h4>Public corpora</h4>
 | 
			
		||||
              <div class="public-corpora-list" data-user-id="{{ user.hashid }}"></div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
{% endblock page_content %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user