diff --git a/app/corpora/routes.py b/app/corpora/routes.py index 8d321c6f..17d203f1 100644 --- a/app/corpora/routes.py +++ b/app/corpora/routes.py @@ -74,15 +74,15 @@ def disable_corpus_is_public(corpus_id): @bp.route('//followers//unfollow', methods=['POST']) @login_required def unfollow_corpus(corpus_id, follower_id): - corpus_follower_association = CorpusFollowerAssociation.query.filter_by(followed_corpus_id=corpus_id, following_user_id=follower_id).first_or_404() - if not (corpus_follower_association.followed_corpus.user == current_user - or corpus_follower_association.following_user == current_user - or current_user.is_administrator()): + corpus = Corpus.query.get_or_404(corpus_id) + follower = User.query.get_or_404(follower_id) + if not (corpus.user == current_user or follower == current_user or current_user.is_administrator()): abort(403) - if not corpus_follower_association.following_user.is_following_corpus(corpus_follower_association.followed_corpus): + if not follower.is_following_corpus(corpus): abort(409) # 'User is not following the corpus' - corpus_follower_association.following_user.unfollow_corpus(corpus_follower_association.followed_corpus) + follower.unfollow_corpus(corpus) db.session.commit() + flash(f'{follower.username} is not following {corpus.title} anymore', category='corpus') return '', 204 @@ -103,10 +103,9 @@ def add_permission(corpus_id, follower_id, permission_name): try: permission = CorpusFollowerPermission[permission_name] except KeyError: - abort(409) # 'Permission "{permission_name}" does not exist' - corpus_follower_association = CorpusFollowerAssociation.query.filter_by(followed_corpus_id=corpus_id, following_user_id=follower_id).first_or_404() - if not (corpus_follower_association.followed_corpus.user == current_user - or current_user.is_administrator()): + abort(409) # f'Permission "{permission_name}" does not exist' + 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() @@ -119,9 +118,8 @@ def remove_permission(corpus_id, follower_id, permission_name): permission = CorpusFollowerPermission[permission_name] except KeyError: return make_response(f'Permission "{permission_name}" does not exist', 409) - corpus_follower_association = CorpusFollowerAssociation.query.filter_by(followed_corpus_id=corpus_id, following_user_id=follower_id).first_or_404() - if not (corpus_follower_association.followed_corpus.user == current_user - or current_user.is_administrator()): + 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) db.session.commit() diff --git a/app/models.py b/app/models.py index 81da0eb3..5e2bdcdf 100644 --- a/app/models.py +++ b/app/models.py @@ -296,16 +296,16 @@ class CorpusFollowerAssociation(HashidMixin, db.Model): # Primary key id = db.Column(db.Integer, primary_key=True) # Foreign keys - following_user_id = db.Column(db.Integer, db.ForeignKey('users.id')) - followed_corpus_id = db.Column(db.Integer, db.ForeignKey('corpora.id')) + 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) # Relationships - followed_corpus = db.relationship('Corpus', back_populates='following_user_associations') - following_user = db.relationship('User', back_populates='followed_corpus_associations') + corpus = db.relationship('Corpus', back_populates='corpus_follower_associations') + follower = db.relationship('User', back_populates='corpus_follower_associations') def __repr__(self): - return f'' + return f'' def has_permission(self, permission: CorpusFollowerPermission): return self.permissions & permission.value == permission.value @@ -325,8 +325,8 @@ class CorpusFollowerAssociation(HashidMixin, db.Model): x.name for x in CorpusFollowerPermission if self.has_permission(x) ], - 'followed_corpus': self.followed_corpus.to_json_serializeable(), - 'following_user': self.following_user.to_json_serializeable() + 'corpus': self.corpus.to_json_serializeable(), + 'follower': self.follower.to_json_serializeable() } return json_serializeable @@ -368,15 +368,15 @@ class User(HashidMixin, UserMixin, db.Model): cascade='all, delete-orphan', lazy='dynamic' ) - followed_corpus_associations = db.relationship( + corpus_follower_associations = db.relationship( 'CorpusFollowerAssociation', - back_populates='following_user', + back_populates='follower', cascade='all, delete-orphan' ) followed_corpora = association_proxy( - 'followed_corpus_associations', - 'followed_corpus', - creator=lambda c: CorpusFollowerAssociation(followed_corpus=c) + 'corpus_follower_associations', + 'corpus', + creator=lambda c: CorpusFollowerAssociation(corpus=c) ) jobs = db.relationship( 'Job', @@ -652,9 +652,9 @@ class User(HashidMixin, UserMixin, db.Model): json_serializeable['role'] = \ self.role.to_json_serializeable(backrefs=True) if relationships: - json_serializeable['followed_corpus_associations'] = { + json_serializeable['corpus_follower_associations'] = { x.hashid: x.to_json_serializeable() - for x in self.followed_corpus_associations + for x in self.corpus_follower_associations } json_serializeable['corpora'] = { x.hashid: x.to_json_serializeable(relationships=True) @@ -1319,15 +1319,15 @@ class Corpus(HashidMixin, db.Model): lazy='dynamic', cascade='all, delete-orphan' ) - following_user_associations = db.relationship( + corpus_follower_associations = db.relationship( 'CorpusFollowerAssociation', - back_populates='followed_corpus', + back_populates='corpus', cascade='all, delete-orphan' ) - following_users = association_proxy( - 'following_user_associations', - 'following_user', - creator=lambda u: CorpusFollowerAssociation(following_user=u) + followers = association_proxy( + 'corpus_follower_associations', + 'follower', + creator=lambda u: CorpusFollowerAssociation(followers=u) ) user = db.relationship('User', back_populates='corpora') # "static" attributes @@ -1429,9 +1429,9 @@ class Corpus(HashidMixin, db.Model): json_serializeable['user'] = \ self.user.to_json_serializeable(backrefs=True) if relationships: - json_serializeable['following_user_associations'] = { + json_serializeable['corpus_follower_associations'] = { x.hashid: x.to_json_serializeable() - for x in self.following_user_associations + for x in self.corpus_follower_associations } json_serializeable['files'] = { x.hashid: x.to_json_serializeable(relationships=True) diff --git a/app/static/js/ResourceLists/CorpusFollowerList.js b/app/static/js/ResourceLists/CorpusFollowerList.js index 711d5111..d83e4a39 100644 --- a/app/static/js/ResourceLists/CorpusFollowerList.js +++ b/app/static/js/ResourceLists/CorpusFollowerList.js @@ -19,7 +19,7 @@ class CorpusFollowerList extends ResourceList { }); }); app.getUser(this.userId).then((user) => { - this.add(Object.values(user.corpora[this.corpusId].following_user_associations)); + this.add(Object.values(user.corpora[this.corpusId].corpus_follower_associations)); this.isInitialized = true; }); } @@ -32,20 +32,26 @@ class CorpusFollowerList extends ResourceList {
- + + +
- + + +
- + + + delete @@ -95,14 +101,13 @@ class CorpusFollowerList extends ResourceList { } mapResourceToValue(corpusFollowerAssociation) { - let user = corpusFollowerAssociation.following_user; return { 'id': corpusFollowerAssociation.id, - 'follower-id': user.id, - 'avatar': user.avatar ? `/users/${user.id}/avatar` : '/static/images/user_avatar.png', - 'username': user.username, - 'full-name': user.full_name ? user.full_name : '', - 'about-me': user.about_me ? user.about_me : '', + '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') @@ -141,6 +146,7 @@ class CorpusFollowerList 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; diff --git a/migrations/versions/03c7211f089d_.py b/migrations/versions/03c7211f089d_.py new file mode 100644 index 00000000..2f73e3aa --- /dev/null +++ b/migrations/versions/03c7211f089d_.py @@ -0,0 +1,42 @@ +"""empty message + +Revision ID: 03c7211f089d +Revises: 5fe6a6c7870c +Create Date: 2023-02-21 09:57:22.005883 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '03c7211f089d' +down_revision = '5fe6a6c7870c' +branch_labels = None +depends_on = None + + +def upgrade(): + op.alter_column( + 'corpus_follower_associations', + 'followed_corpus_id', + new_column_name='corpus_id' + ) + op.alter_column( + 'corpus_follower_associations', + 'following_user_id', + new_column_name='follower_id' + ) + + +def downgrade(): + op.alter_column( + 'corpus_follower_associations', + 'corpus_id', + new_column_name='followed_corpus_id' + ) + op.alter_column( + 'corpus_follower_associations', + 'follower_id', + new_column_name='following_user_id' + )