mirror of
				https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
				synced 2025-11-03 20:02:47 +00:00 
			
		
		
		
	Integrate CorpusFollowerRoles
This commit is contained in:
		
							
								
								
									
										17
									
								
								app/cli.py
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								app/cli.py
									
									
									
									
									
								
							@@ -3,10 +3,11 @@ from flask_migrate import upgrade
 | 
			
		||||
import click
 | 
			
		||||
import os
 | 
			
		||||
from app.models import (
 | 
			
		||||
    CorpusFollowerRole,
 | 
			
		||||
    Role,
 | 
			
		||||
    User,
 | 
			
		||||
    SpaCyNLPPipelineModel,
 | 
			
		||||
    TesseractOCRPipelineModel,
 | 
			
		||||
    SpaCyNLPPipelineModel
 | 
			
		||||
    User
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -30,19 +31,23 @@ def register(app):
 | 
			
		||||
    def deploy():
 | 
			
		||||
        ''' Run deployment tasks. '''
 | 
			
		||||
        # Make default directories
 | 
			
		||||
        print('Make default directories')
 | 
			
		||||
        _make_default_dirs()
 | 
			
		||||
 | 
			
		||||
        # migrate database to latest revision
 | 
			
		||||
        print('Migrate database to latest revision')
 | 
			
		||||
        upgrade()
 | 
			
		||||
 | 
			
		||||
        # Insert/Update default database values
 | 
			
		||||
        current_app.logger.info('Insert/Update default roles')
 | 
			
		||||
        print('Insert/Update default Roles')
 | 
			
		||||
        Role.insert_defaults()
 | 
			
		||||
        current_app.logger.info('Insert/Update default users')
 | 
			
		||||
        print('Insert/Update default Users')
 | 
			
		||||
        User.insert_defaults()
 | 
			
		||||
        current_app.logger.info('Insert/Update default SpaCyNLPPipelineModels')
 | 
			
		||||
        print('Insert/Update default CorpusFollowerRoles')
 | 
			
		||||
        CorpusFollowerRole.insert_defaults()
 | 
			
		||||
        print('Insert/Update default SpaCyNLPPipelineModels')
 | 
			
		||||
        SpaCyNLPPipelineModel.insert_defaults()
 | 
			
		||||
        current_app.logger.info('Insert/Update default TesseractOCRPipelineModels')
 | 
			
		||||
        print('Insert/Update default TesseractOCRPipelineModels')
 | 
			
		||||
        TesseractOCRPipelineModel.insert_defaults()
 | 
			
		||||
 | 
			
		||||
    @app.cli.group()
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ from app.models import (
 | 
			
		||||
    CorpusFile,
 | 
			
		||||
    CorpusFollowerAssociation,
 | 
			
		||||
    CorpusFollowerPermission,
 | 
			
		||||
    CorpusFollowerRole,
 | 
			
		||||
    CorpusStatus,
 | 
			
		||||
    User
 | 
			
		||||
)
 | 
			
		||||
@@ -107,30 +108,16 @@ def current_user_unfollow_corpus(corpus_id):
 | 
			
		||||
    return '', 204
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/followers/<hashid:follower_id>/permissions/<permission_name>/add', methods=['POST'])
 | 
			
		||||
def add_permission(corpus_id, follower_id, permission_name):
 | 
			
		||||
    try:
 | 
			
		||||
        permission = CorpusFollowerPermission[permission_name]
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        abort(409)  # f'Permission "{permission_name}" does not exist'
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/followers/<hashid:follower_id>/role', methods=['POST'])
 | 
			
		||||
def add_permission(corpus_id, follower_id):
 | 
			
		||||
    corpus_follower_association = CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id, follower_id=follower_id).first_or_404()
 | 
			
		||||
    if not (corpus_follower_association.corpus.user == current_user or current_user.is_administrator()):
 | 
			
		||||
        abort(403)
 | 
			
		||||
    corpus_follower_association.add_permission(permission)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return '', 204
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:corpus_id>/followers/<hashid:follower_id>/permissions/<permission_name>/remove', methods=['POST'])
 | 
			
		||||
def remove_permission(corpus_id, follower_id, permission_name):
 | 
			
		||||
    try:
 | 
			
		||||
        permission = CorpusFollowerPermission[permission_name]
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        return make_response(f'Permission "{permission_name}" does not exist', 409)
 | 
			
		||||
    corpus_follower_association = CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id, follower_id=follower_id).first_or_404()
 | 
			
		||||
    if not (corpus_follower_association.corpus.user == current_user or current_user.is_administrator()):
 | 
			
		||||
        abort(403)
 | 
			
		||||
    corpus_follower_association.remove_permission(permission)
 | 
			
		||||
    role_name = request.json.get('role')
 | 
			
		||||
    if role_name is None:
 | 
			
		||||
        abort(400)
 | 
			
		||||
    corpus_follower_role = CorpusFollowerRole.query.filter_by(name=role_name).first_or_404()
 | 
			
		||||
    corpus_follower_association.role = corpus_follower_role
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return '', 204
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										191
									
								
								app/models.py
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								app/models.py
									
									
									
									
									
								
							@@ -6,6 +6,7 @@ from flask_login import UserMixin
 | 
			
		||||
from sqlalchemy.ext.associationproxy import association_proxy
 | 
			
		||||
from time import sleep
 | 
			
		||||
from tqdm import tqdm
 | 
			
		||||
from typing import Union
 | 
			
		||||
from werkzeug.security import generate_password_hash, check_password_hash
 | 
			
		||||
from werkzeug.utils import secure_filename
 | 
			
		||||
import json
 | 
			
		||||
@@ -57,6 +58,15 @@ class Permission(IntEnum):
 | 
			
		||||
    CONTRIBUTE = 2
 | 
			
		||||
    USE_API = 4
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get(permission: Union['Permission', int, str]) -> 'Permission':
 | 
			
		||||
        if isinstance(permission, Permission):
 | 
			
		||||
            return permission
 | 
			
		||||
        if isinstance(permission, int):
 | 
			
		||||
            return Permission(permission)
 | 
			
		||||
        if isinstance(permission, str):
 | 
			
		||||
            return Permission[permission]
 | 
			
		||||
        raise TypeError('permission must be Permission, int, or str')
 | 
			
		||||
 | 
			
		||||
class UserSettingJobStatusMailNotificationLevel(IntEnum):
 | 
			
		||||
    NONE = 1
 | 
			
		||||
@@ -72,8 +82,22 @@ class ProfilePrivacySettings(IntEnum):
 | 
			
		||||
 | 
			
		||||
class CorpusFollowerPermission(IntEnum):
 | 
			
		||||
    VIEW = 1
 | 
			
		||||
    CONTRIBUTE = 2
 | 
			
		||||
    ADMINISTRATE = 4
 | 
			
		||||
    ADD_CORPUS_FILE = 2
 | 
			
		||||
    UPDATE_CORPUS_FILE = 4
 | 
			
		||||
    REMOVE_CORPUS_FILE = 8
 | 
			
		||||
    GENERATE_SHARE_LINK = 16
 | 
			
		||||
    REMOVE_FOLLOWER = 32
 | 
			
		||||
    UPDATE_FOLLOWER = 64
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get(permission: Union['CorpusFollowerPermission', int, str]) -> 'CorpusFollowerPermission':
 | 
			
		||||
        if isinstance(permission, CorpusFollowerPermission):
 | 
			
		||||
            return permission
 | 
			
		||||
        if isinstance(permission, int):
 | 
			
		||||
            return CorpusFollowerPermission(permission)
 | 
			
		||||
        if isinstance(permission, str):
 | 
			
		||||
            return CorpusFollowerPermission[permission]
 | 
			
		||||
        raise TypeError('permission must be CorpusFollowerPermission, int, or str')
 | 
			
		||||
# endregion enums
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -181,16 +205,19 @@ class Role(HashidMixin, db.Model):
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return f'<Role {self.name}>'
 | 
			
		||||
 | 
			
		||||
    def add_permission(self, permission):
 | 
			
		||||
        if not self.has_permission(permission):
 | 
			
		||||
            self.permissions += permission
 | 
			
		||||
 | 
			
		||||
    def has_permission(self, permission):
 | 
			
		||||
        return self.permissions & permission == permission
 | 
			
		||||
 | 
			
		||||
    def remove_permission(self, permission):
 | 
			
		||||
        if self.has_permission(permission):
 | 
			
		||||
            self.permissions -= permission
 | 
			
		||||
    def has_permission(self, permission: Union[Permission, int, str]):
 | 
			
		||||
        perm = Permission.get(permission)
 | 
			
		||||
        return self.permissions & perm.value == perm.value
 | 
			
		||||
    
 | 
			
		||||
    def add_permission(self, permission: Union[Permission, int, str]):
 | 
			
		||||
        perm = Permission.get(permission)
 | 
			
		||||
        if not self.has_permission(perm):
 | 
			
		||||
            self.permissions += perm.value
 | 
			
		||||
    
 | 
			
		||||
    def remove_permission(self, permission: Union[Permission, int, str]):
 | 
			
		||||
        perm = Permission.get(permission)
 | 
			
		||||
        if self.has_permission(perm):
 | 
			
		||||
            self.permissions -= perm.value
 | 
			
		||||
 | 
			
		||||
    def reset_permissions(self):
 | 
			
		||||
        self.permissions = 0
 | 
			
		||||
@@ -319,6 +346,96 @@ class Avatar(HashidMixin, FileMixin, db.Model):
 | 
			
		||||
        return json_serializeable
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CorpusFollowerRole(HashidMixin, db.Model):
 | 
			
		||||
    __tablename__ = 'corpus_follower_roles'
 | 
			
		||||
    # Primary key
 | 
			
		||||
    id = db.Column(db.Integer, primary_key=True)
 | 
			
		||||
    # Fields
 | 
			
		||||
    name = db.Column(db.String(64), unique=True)
 | 
			
		||||
    default = db.Column(db.Boolean, default=False, index=True)
 | 
			
		||||
    permissions = db.Column(db.Integer, default=0)
 | 
			
		||||
    # Relationships
 | 
			
		||||
    corpus_follower_associations = db.relationship(
 | 
			
		||||
        'CorpusFollowerAssociation',
 | 
			
		||||
        back_populates='role',
 | 
			
		||||
        lazy='dynamic'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return f'<CorpusFollowerRole {self.name}>'
 | 
			
		||||
 | 
			
		||||
    def has_permission(self, permission: Union[CorpusFollowerPermission, int, str]):
 | 
			
		||||
        perm = CorpusFollowerPermission.get(permission)
 | 
			
		||||
        return self.permissions & perm.value == perm.value
 | 
			
		||||
    
 | 
			
		||||
    def add_permission(self, permission: Union[CorpusFollowerPermission, int, str]):
 | 
			
		||||
        perm = CorpusFollowerPermission.get(permission)
 | 
			
		||||
        if not self.has_permission(perm):
 | 
			
		||||
            self.permissions += perm.value
 | 
			
		||||
    
 | 
			
		||||
    def remove_permission(self, permission: Union[CorpusFollowerPermission, int, str]):
 | 
			
		||||
        perm = CorpusFollowerPermission.get(permission)
 | 
			
		||||
        if self.has_permission(perm):
 | 
			
		||||
            self.permissions -= perm.value
 | 
			
		||||
 | 
			
		||||
    def reset_permissions(self):
 | 
			
		||||
        self.permissions = 0
 | 
			
		||||
 | 
			
		||||
    def to_json_serializeable(self, backrefs=False, relationships=False):
 | 
			
		||||
        json_serializeable = {
 | 
			
		||||
            'id': self.hashid,
 | 
			
		||||
            'default': self.default,
 | 
			
		||||
            'name': self.name,
 | 
			
		||||
            'permissions': [
 | 
			
		||||
                x.name
 | 
			
		||||
                for x in CorpusFollowerPermission
 | 
			
		||||
                if self.has_permission(x)
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
        if backrefs:
 | 
			
		||||
            pass
 | 
			
		||||
        if relationships:
 | 
			
		||||
            json_serializeable['corpus_follower_association'] = {
 | 
			
		||||
                x.hashid: x.to_json_serializeable(relationships=True)
 | 
			
		||||
                for x in self.corpus_follower_association
 | 
			
		||||
            }
 | 
			
		||||
        return json_serializeable
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def insert_defaults():
 | 
			
		||||
        roles = {
 | 
			
		||||
            'Viewer': [
 | 
			
		||||
                CorpusFollowerPermission.VIEW
 | 
			
		||||
            ],
 | 
			
		||||
            'Contributor': [
 | 
			
		||||
                CorpusFollowerPermission.VIEW,
 | 
			
		||||
                CorpusFollowerPermission.ADD_CORPUS_FILE,
 | 
			
		||||
                CorpusFollowerPermission.UPDATE_CORPUS_FILE,
 | 
			
		||||
                CorpusFollowerPermission.REMOVE_CORPUS_FILE
 | 
			
		||||
            ],
 | 
			
		||||
            'Administrator': [
 | 
			
		||||
                CorpusFollowerPermission.VIEW,
 | 
			
		||||
                CorpusFollowerPermission.ADD_CORPUS_FILE,
 | 
			
		||||
                CorpusFollowerPermission.UPDATE_CORPUS_FILE,
 | 
			
		||||
                CorpusFollowerPermission.REMOVE_CORPUS_FILE,
 | 
			
		||||
                CorpusFollowerPermission.GENERATE_SHARE_LINK,
 | 
			
		||||
                CorpusFollowerPermission.REMOVE_FOLLOWER,
 | 
			
		||||
                CorpusFollowerPermission.UPDATE_FOLLOWER
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
        default_role_name = 'Viewer'
 | 
			
		||||
        for role_name, permissions in roles.items():
 | 
			
		||||
            role = CorpusFollowerRole.query.filter_by(name=role_name).first()
 | 
			
		||||
            if role is None:
 | 
			
		||||
                role = CorpusFollowerRole(name=role_name)
 | 
			
		||||
            role.reset_permissions()
 | 
			
		||||
            for permission in permissions:
 | 
			
		||||
                role.add_permission(permission)
 | 
			
		||||
            role.default = role.name == default_role_name
 | 
			
		||||
            db.session.add(role)
 | 
			
		||||
        db.session.commit()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CorpusFollowerAssociation(HashidMixin, db.Model):
 | 
			
		||||
    __tablename__ = 'corpus_follower_associations'
 | 
			
		||||
    # Primary key
 | 
			
		||||
@@ -326,41 +443,38 @@ class CorpusFollowerAssociation(HashidMixin, db.Model):
 | 
			
		||||
    # Foreign keys
 | 
			
		||||
    corpus_id = db.Column(db.Integer, db.ForeignKey('corpora.id'))
 | 
			
		||||
    follower_id = db.Column(db.Integer, db.ForeignKey('users.id'))
 | 
			
		||||
    # Fields
 | 
			
		||||
    permissions = db.Column(db.Integer, default=0, nullable=False)
 | 
			
		||||
    role_id = db.Column(db.Integer, db.ForeignKey('corpus_follower_roles.id'))
 | 
			
		||||
    # Relationships
 | 
			
		||||
    corpus = db.relationship('Corpus', back_populates='corpus_follower_associations')
 | 
			
		||||
    follower = db.relationship('User', back_populates='corpus_follower_associations')
 | 
			
		||||
    corpus = db.relationship(
 | 
			
		||||
        'Corpus',
 | 
			
		||||
        back_populates='corpus_follower_associations'
 | 
			
		||||
    )
 | 
			
		||||
    follower = db.relationship(
 | 
			
		||||
        'User',
 | 
			
		||||
        back_populates='corpus_follower_associations'
 | 
			
		||||
    )
 | 
			
		||||
    role = db.relationship(
 | 
			
		||||
        'CorpusFollowerRole',
 | 
			
		||||
        back_populates='corpus_follower_associations'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        super().__init__(**kwargs)
 | 
			
		||||
        if self.role is None:
 | 
			
		||||
            self.role = CorpusFollowerRole.query.filter_by(default=True).first()
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return f'<CorpusFollowerAssociation {self.follower.__repr__()} ~ {self.corpus.__repr__()}>'
 | 
			
		||||
 | 
			
		||||
    def has_permission(self, permission: CorpusFollowerPermission):
 | 
			
		||||
        return self.permissions & permission.value == permission.value
 | 
			
		||||
    
 | 
			
		||||
    def add_permission(self, permission: CorpusFollowerPermission):
 | 
			
		||||
        if not self.has_permission(permission):
 | 
			
		||||
            self.permissions += permission.value
 | 
			
		||||
    
 | 
			
		||||
    def remove_permission(self, permission: CorpusFollowerPermission):
 | 
			
		||||
        if self.has_permission(permission):
 | 
			
		||||
            self.permissions -= permission.value
 | 
			
		||||
 | 
			
		||||
    def to_json_serializeable(self, backrefs=False, relationships=False):
 | 
			
		||||
        json_serializeable = {
 | 
			
		||||
            'id': self.hashid,
 | 
			
		||||
            'permissions': [
 | 
			
		||||
                x.name for x in CorpusFollowerPermission
 | 
			
		||||
                if self.has_permission(x)
 | 
			
		||||
            ],
 | 
			
		||||
            'corpus': self.corpus.to_json_serializeable(),
 | 
			
		||||
            'follower': self.follower.to_json_serializeable()
 | 
			
		||||
            'follower': self.follower.to_json_serializeable(),
 | 
			
		||||
            'role': self.role.to_json_serializeable()
 | 
			
		||||
        }
 | 
			
		||||
        if backrefs:
 | 
			
		||||
            json_serializeable['corpus'] = \
 | 
			
		||||
                self.corpus.to_json_serializeable(backrefs=True)
 | 
			
		||||
            json_serializeable['follower'] = \
 | 
			
		||||
                self.follower.to_json_serializeable(backrefs=True)
 | 
			
		||||
            pass
 | 
			
		||||
        if relationships:
 | 
			
		||||
            pass
 | 
			
		||||
        return json_serializeable
 | 
			
		||||
@@ -559,7 +673,6 @@ class User(HashidMixin, UserMixin, db.Model):
 | 
			
		||||
                issuer=current_app.config['SERVER_NAME'],
 | 
			
		||||
                options={'require': ['exp', 'iat', 'iss', 'purpose', 'sub']}
 | 
			
		||||
            )
 | 
			
		||||
            current_app.logger.warning(payload)
 | 
			
		||||
        except jwt.PyJWTError:
 | 
			
		||||
            return False
 | 
			
		||||
        if payload.get('purpose') != 'user.confirm':
 | 
			
		||||
@@ -687,7 +800,7 @@ class User(HashidMixin, UserMixin, db.Model):
 | 
			
		||||
                self.role.to_json_serializeable(backrefs=True)
 | 
			
		||||
        if relationships:
 | 
			
		||||
            json_serializeable['corpus_follower_associations'] = {
 | 
			
		||||
                x.hashid: x.to_json_serializeable(relationships=True)
 | 
			
		||||
                x.hashid: x.to_json_serializeable()
 | 
			
		||||
                for x in self.corpus_follower_associations
 | 
			
		||||
            }
 | 
			
		||||
            json_serializeable['corpora'] = {
 | 
			
		||||
@@ -1469,7 +1582,7 @@ class Corpus(HashidMixin, db.Model):
 | 
			
		||||
                self.user.to_json_serializeable(backrefs=True)
 | 
			
		||||
        if relationships:
 | 
			
		||||
            json_serializeable['corpus_follower_associations'] = {
 | 
			
		||||
                x.hashid: x.to_json_serializeable(relationships=True)
 | 
			
		||||
                x.hashid: x.to_json_serializeable()
 | 
			
		||||
                for x in self.corpus_follower_associations
 | 
			
		||||
            }
 | 
			
		||||
            json_serializeable['files'] = {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,9 @@ class CorpusFollowerList extends ResourceList {
 | 
			
		||||
 | 
			
		||||
  constructor(listContainerElement, options = {}) {
 | 
			
		||||
    super(listContainerElement, options);
 | 
			
		||||
    this.listjs.on('updated', () => {
 | 
			
		||||
      M.FormSelect.init(this.listjs.list.querySelectorAll('.list-item select'));
 | 
			
		||||
    });
 | 
			
		||||
    this.listjs.list.addEventListener('change', (event) => {this.onChange(event)});
 | 
			
		||||
    this.listjs.list.addEventListener('click', (event) => {this.onClick(event)});
 | 
			
		||||
    this.isInitialized = false;
 | 
			
		||||
@@ -28,30 +31,21 @@ class CorpusFollowerList extends ResourceList {
 | 
			
		||||
    return (values) => {
 | 
			
		||||
      return `
 | 
			
		||||
        <tr class="list-item clickable hoverable">
 | 
			
		||||
          <td><img alt="user-image" class="circle responsive-img avatar" style="width:50%"></td>
 | 
			
		||||
          <td><b class="username"><b></td>
 | 
			
		||||
          <td><span class="full-name"></span><br><i class="about-me"></i></td>
 | 
			
		||||
          <td><img alt="follower-avatar" class="circle responsive-img follower-avatar" style="width:50%"></td>
 | 
			
		||||
          <td><b class="follower-username"><b></td>
 | 
			
		||||
          <td>
 | 
			
		||||
            <span class="disable-on-click">
 | 
			
		||||
              <label>
 | 
			
		||||
                <input ${values['permission-can-VIEW'] ? 'checked' : ''} class="permission-can-VIEW list-action-trigger" data-list-action="toggle-permission" data-permission="VIEW" type="checkbox">
 | 
			
		||||
                <span>View</span>
 | 
			
		||||
              </label>
 | 
			
		||||
            </span>
 | 
			
		||||
            <span class="follower-full-name"></span>
 | 
			
		||||
            <br>
 | 
			
		||||
            <span class="disable-on-click">
 | 
			
		||||
              <label>
 | 
			
		||||
                <input ${values['permission-can-CONTRIBUTE'] ? 'checked' : ''} class="permission-can-CONTRIBUTE list-action-trigger" data-list-action="toggle-permission" data-permission="CONTRIBUTE" type="checkbox">
 | 
			
		||||
                <span>Contribute</span>
 | 
			
		||||
              </label>
 | 
			
		||||
            </span>
 | 
			
		||||
            <br>
 | 
			
		||||
            <span class="disable-on-click">
 | 
			
		||||
              <label>
 | 
			
		||||
                <input ${values['permission-can-ADMINISTRATE'] ? 'checked' : ''} class="permission-can-ADMINISTRATE list-action-trigger" data-list-action="toggle-permission" data-permission="ADMINISTRATE" type="checkbox">
 | 
			
		||||
                <span>Administrate</span>
 | 
			
		||||
              </label>
 | 
			
		||||
            </span>
 | 
			
		||||
            <i class="follower-about-me"></i>
 | 
			
		||||
          </td>
 | 
			
		||||
          <td>
 | 
			
		||||
            <div class="input-field disable-on-click list-action-trigger" data-list-action="update-role">
 | 
			
		||||
              <select>
 | 
			
		||||
                <option value="Viewer" ${values['role.name'] === 'Viewer' ? 'selected' : ''}>Viewer</option>
 | 
			
		||||
                <option value="Contributor" ${values['role.name'] === 'Contributor' ? 'selected' : ''}>Contributor</option>
 | 
			
		||||
                <option value="Administrator" ${values['role.name'] === 'Administrator' ? 'selected' : ''}>Administrator</option>
 | 
			
		||||
              </select>
 | 
			
		||||
            </div>
 | 
			
		||||
          </td>
 | 
			
		||||
          <td class="right-align">
 | 
			
		||||
            <a class="list-action-trigger btn-floating red waves-effect waves-light" data-list-action="unfollow-request"><i class="material-icons">delete</i></a>
 | 
			
		||||
@@ -66,10 +60,10 @@ class CorpusFollowerList extends ResourceList {
 | 
			
		||||
    return [
 | 
			
		||||
      {data: ['id']},
 | 
			
		||||
      {data: ['follower-id']},
 | 
			
		||||
      {name: 'avatar', attr: 'src'},
 | 
			
		||||
      'username',
 | 
			
		||||
      'about-me',
 | 
			
		||||
      'full-name'
 | 
			
		||||
      {name: 'follower-avatar', attr: 'src'},
 | 
			
		||||
      'follower-username',
 | 
			
		||||
      'follower-about-me',
 | 
			
		||||
      'follower-full-name'
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -90,7 +84,7 @@ class CorpusFollowerList extends ResourceList {
 | 
			
		||||
            <th style="width:15%;"></th>
 | 
			
		||||
            <th>Username</th>
 | 
			
		||||
            <th>User details</th>
 | 
			
		||||
            <th>Permissions</th>
 | 
			
		||||
            <th>Role</th>
 | 
			
		||||
            <th></th>
 | 
			
		||||
          </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
@@ -104,13 +98,11 @@ class CorpusFollowerList extends ResourceList {
 | 
			
		||||
    return {
 | 
			
		||||
      'id': corpusFollowerAssociation.id,
 | 
			
		||||
      'follower-id': corpusFollowerAssociation.follower.id,
 | 
			
		||||
      'avatar': corpusFollowerAssociation.follower.avatar ? `/users/${corpusFollowerAssociation.follower.id}/avatar` : '/static/images/user_avatar.png',
 | 
			
		||||
      'username': corpusFollowerAssociation.follower.username,
 | 
			
		||||
      'full-name': corpusFollowerAssociation.follower.full_name ? corpusFollowerAssociation.follower.full_name : '',
 | 
			
		||||
      'about-me': corpusFollowerAssociation.follower.about_me ? corpusFollowerAssociation.follower.about_me : '',
 | 
			
		||||
      'permission-can-VIEW': corpusFollowerAssociation.permissions.includes('VIEW'),
 | 
			
		||||
      'permission-can-CONTRIBUTE': corpusFollowerAssociation.permissions.includes('CONTRIBUTE'),
 | 
			
		||||
      'permission-can-ADMINISTRATE': corpusFollowerAssociation.permissions.includes('ADMINISTRATE')
 | 
			
		||||
      'follower-avatar': corpusFollowerAssociation.follower.avatar ? `/users/${corpusFollowerAssociation.follower.id}/avatar` : '/static/images/user_avatar.png',
 | 
			
		||||
      'follower-username': corpusFollowerAssociation.follower.username,
 | 
			
		||||
      'follower-full-name': corpusFollowerAssociation.follower.full_name ? corpusFollowerAssociation.follower.full_name : '',
 | 
			
		||||
      'follower-about-me': corpusFollowerAssociation.follower.about_me ? corpusFollowerAssociation.follower.about_me : '',
 | 
			
		||||
      'role-name': corpusFollowerAssociation.role.name
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -119,7 +111,7 @@ class CorpusFollowerList extends ResourceList {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onChange(event) {
 | 
			
		||||
    if (event.target.tagName !== 'INPUT') {return;}
 | 
			
		||||
    console.log(event.target.tagName);
 | 
			
		||||
    let listItemElement = event.target.closest('.list-item[data-id]');
 | 
			
		||||
    if (listItemElement === null) {return;}
 | 
			
		||||
    let itemId = listItemElement.dataset.id;
 | 
			
		||||
@@ -127,16 +119,10 @@ class CorpusFollowerList extends ResourceList {
 | 
			
		||||
    if (listActionElement === null) {return;}
 | 
			
		||||
    let listAction = listActionElement.dataset.listAction;
 | 
			
		||||
    switch (listAction) {
 | 
			
		||||
      case 'toggle-permission': {
 | 
			
		||||
      case 'update-role': {
 | 
			
		||||
        let followerId = listItemElement.dataset.followerId;
 | 
			
		||||
        let permission = listActionElement.dataset.permission;
 | 
			
		||||
        if (event.target.checked) {
 | 
			
		||||
          Utils.addCorpusFollowerPermissionRequest(this.corpusId, followerId, permission)
 | 
			
		||||
            .catch((error) => {event.target.checked = !event.target.checked;});
 | 
			
		||||
        } else {
 | 
			
		||||
          Utils.removeCorpusFollowerPermissionRequest(this.corpusId, followerId, permission)
 | 
			
		||||
            .catch((error) => {event.target.checked = !event.target.checked;});
 | 
			
		||||
        }
 | 
			
		||||
        let roleName = event.target.value;
 | 
			
		||||
        Utils.updateCorpusFollowerRole(this.corpusId, followerId, roleName);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      default: {
 | 
			
		||||
@@ -188,11 +174,11 @@ class CorpusFollowerList extends ResourceList {
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        case 'replace': {
 | 
			
		||||
          // let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/corpus_follower_associations/([A-Za-z0-9]*)/(service|status|description|title)$`);
 | 
			
		||||
          // if (re.test(operation.path)) {
 | 
			
		||||
          //   let [match, jobId, valueName] = operation.path.match(re);
 | 
			
		||||
          //   this.replace(jobId, valueName, operation.value);
 | 
			
		||||
          // }
 | 
			
		||||
          let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/corpus_follower_associations/([A-Za-z0-9]*)/role$`);
 | 
			
		||||
          if (re.test(operation.path)) {
 | 
			
		||||
            let [match, jobId, valueName] = operation.path.match(re);
 | 
			
		||||
            this.replace(jobId, valueName, operation.value);
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        default: {
 | 
			
		||||
 
 | 
			
		||||
@@ -36,13 +36,13 @@
 | 
			
		||||
  <li><a href="{{ url_for('settings.settings') }}"><i class="material-icons">settings</i>General Settings</a></li>
 | 
			
		||||
  <li><a href="{{ url_for('users.edit_profile', user_id=current_user.id) }}"><i class="material-icons left">contact_page</i>Profile settings</a></li>
 | 
			
		||||
  <li><a href="{{ url_for('auth.logout') }}">Log out</a></li>
 | 
			
		||||
  {% if current_user.can(Permission.ADMINISTRATE) or current_user.can(Permission.USE_API) %}
 | 
			
		||||
  {% if current_user.can('ADMINISTRATE') or current_user.can('USE_API') %}
 | 
			
		||||
  <li><div class="divider"></div></li>
 | 
			
		||||
  {% endif %}
 | 
			
		||||
  {% if current_user.can(Permission.ADMINISTRATE) %}
 | 
			
		||||
  {% if current_user.can('ADMINISTRATE') %}
 | 
			
		||||
  <li><a href="{{ url_for('admin.index') }}"><i class="material-icons">admin_panel_settings</i>Administration</a></li>
 | 
			
		||||
  {% endif %}
 | 
			
		||||
  {% if current_user.can(Permission.USE_API) %}
 | 
			
		||||
  {% if current_user.can('USE_API') %}
 | 
			
		||||
  <li><a href="{{ url_for('apifairy.docs') }}"><i class="material-icons">api</i>API</a></li>
 | 
			
		||||
  {% endif %}
 | 
			
		||||
</ul>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user