]> Untitled Git - lemmy.git/commitdiff
Community moderation fixes.
authorDessalines <tyhou13@gmx.com>
Sun, 21 Apr 2019 20:52:55 +0000 (13:52 -0700)
committerDessalines <tyhou13@gmx.com>
Sun, 21 Apr 2019 20:52:55 +0000 (13:52 -0700)
- Don't show banned communities on main post list. Fixes #95
- Add back in community moderation and editing. Fixes #92

server/migrations/2019-03-30-212058_create_post_view/up.sql
server/src/actions/post_view.rs
server/src/websocket_server/server.rs
ui/src/components/comment-node.tsx
ui/src/components/community.tsx
ui/src/components/post-listing.tsx
ui/src/components/post.tsx
ui/src/components/sidebar.tsx
ui/src/interfaces.ts

index 17dc8604a7667896636e29243cd39156b5084cbb..4a4fd146e56cb1c84e951debfa874fad4e69f5ef 100644 (file)
@@ -16,6 +16,7 @@ with all_post as
   p.*,
   (select name from user_ where p.creator_id = user_.id) as creator_name,
   (select name from community where p.community_id = community.id) as community_name,
+  (select removed from community c where p.community_id = c.id) as community_removed,
   (select count(*) from comment where comment.post_id = p.id) as number_of_comments,
   coalesce(sum(pl.score), 0) as score,
   count (case when pl.score = 1 then 1 else null end) as upvotes,
index ba42fe277b6a99ae0524b89b984e53298f17adcc..ccad931782c686861174507e63b4917224c4c4b0 100644 (file)
@@ -25,6 +25,7 @@ table! {
     updated -> Nullable<Timestamp>,
     creator_name -> Varchar,
     community_name -> Varchar,
+    community_removed -> Bool,
     number_of_comments -> BigInt,
     score -> BigInt,
     upvotes -> BigInt,
@@ -54,6 +55,7 @@ pub struct PostView {
   pub updated: Option<chrono::NaiveDateTime>,
   pub creator_name: String,
   pub community_name: String,
+  pub community_removed: bool,
   pub number_of_comments: i64,
   pub score: i64,
   pub upvotes: i64,
@@ -133,13 +135,11 @@ impl PostView {
               .order_by(score.desc())
     };
 
-
-    // TODO make sure community removed isn't fetched either
-
     query = query
       .limit(limit)
       .offset(offset)
-      .filter(removed.eq(false));
+      .filter(removed.eq(false))
+      .filter(community_removed.eq(false));
 
     query.load::<Self>(conn) 
   }
@@ -255,6 +255,7 @@ mod tests {
       removed: false,
       locked: false,
       community_name: community_name.to_owned(),
+      community_removed: false,
       number_of_comments: 0,
       score: 1,
       upvotes: 1,
@@ -280,6 +281,7 @@ mod tests {
       creator_name: user_name.to_owned(),
       community_id: inserted_community.id,
       community_name: community_name.to_owned(),
+      community_removed: false,
       number_of_comments: 0,
       score: 1,
       upvotes: 1,
index ba6e176b565881fffe4d44c9ae9746e55e5fc3fd..068fd027571369a7773dae7999cb9ddd81e114fc 100644 (file)
@@ -196,7 +196,8 @@ pub struct GetCommunity {
 pub struct GetCommunityResponse {
   op: String,
   community: CommunityView,
-  moderators: Vec<CommunityModeratorView>
+  moderators: Vec<CommunityModeratorView>,
+  admins: Vec<UserView>,
 }
 
 #[derive(Serialize, Deserialize)]
@@ -1165,13 +1166,16 @@ impl Perform for GetCommunity {
       }
     };
 
+    let admins = UserView::admins(&conn)?;
+
     // Return the jwt
     Ok(
       serde_json::to_string(
         &GetCommunityResponse {
           op: self.op_type().to_string(),
           community: community_view,
-          moderators: moderators
+          moderators: moderators,
+          admins: admins,
         }
         )?
       )
@@ -1817,11 +1821,24 @@ impl Perform for EditCommunity {
     }
 
     // Verify its a mod
-    let moderator_view = CommunityModeratorView::for_community(&conn, self.edit_id)?;
-    let mod_ids: Vec<i32> = moderator_view.into_iter().map(|m| m.user_id).collect();
-    if !mod_ids.contains(&user_id) {
-      return Err(self.error("Incorrect creator."))?
-    };
+    let mut editors: Vec<i32> = Vec::new();
+    editors.append(
+      &mut CommunityModeratorView::for_community(&conn, self.edit_id)
+      ?
+      .into_iter()
+      .map(|m| m.user_id)
+      .collect()
+      );
+    editors.append(
+      &mut UserView::admins(&conn)
+      ?
+      .into_iter()
+      .map(|a| a.id)
+      .collect()
+      );
+    if !editors.contains(&user_id) {
+      return Err(self.error("Not allowed to edit community"))?
+    }
 
     let community_form = CommunityForm {
       name: self.name.to_owned(),
index cf7b1bceadd5ecb61d2abad6346d5708ccf826db..415dad77b49ddbdc449dc6de5a65f0d1487e769d 100644 (file)
@@ -210,13 +210,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     return UserService.Instance.user && this.props.node.comment.creator_id == UserService.Instance.user.id;
   }
 
-  get canMod(): boolean {
-    let adminsThenMods = this.props.admins.map(a => a.id)
-    .concat(this.props.moderators.map(m => m.user_id));
-
-    return canMod(UserService.Instance.user, adminsThenMods, this.props.node.comment.creator_id);
-  }
-
   get isMod(): boolean {
     return this.props.moderators && isMod(this.props.moderators.map(m => m.user_id), this.props.node.comment.creator_id);
   }
@@ -225,6 +218,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     return this.props.admins && isMod(this.props.admins.map(a => a.id), this.props.node.comment.creator_id);
   }
 
+  get canMod(): boolean {
+    let adminsThenMods = this.props.admins.map(a => a.id)
+    .concat(this.props.moderators.map(m => m.user_id));
+
+    return canMod(UserService.Instance.user, adminsThenMods, this.props.node.comment.creator_id);
+  }
+
   get canAdmin(): boolean {
     return this.props.admins && canMod(UserService.Instance.user, this.props.admins.map(a => a.id), this.props.node.comment.creator_id);
   }
index 6271bde5a695095071f3b88a9019fec7f7d254b3..c89d2f0624b11519e902381789e47c509eb08ebd 100644 (file)
@@ -1,7 +1,7 @@
 import { Component } from 'inferno';
 import { Subscription } from "rxjs";
 import { retryWhen, delay, take } from 'rxjs/operators';
-import { UserOperation, Community as CommunityI, GetCommunityResponse, CommunityResponse,  CommunityUser} from '../interfaces';
+import { UserOperation, Community as CommunityI, GetCommunityResponse, CommunityResponse,  CommunityUser, UserView } from '../interfaces';
 import { WebSocketService } from '../services';
 import { PostListings } from './post-listings';
 import { Sidebar } from './sidebar';
@@ -11,6 +11,7 @@ interface State {
   community: CommunityI;
   communityId: number;
   moderators: Array<CommunityUser>;
+  admins: Array<UserView>;
   loading: boolean;
 }
 
@@ -29,9 +30,11 @@ export class Community extends Component<any, State> {
       number_of_subscribers: null,
       number_of_posts: null,
       number_of_comments: null,
-      published: null
+      published: null,
+      removed: null,
     },
     moderators: [],
+    admins: [],
     communityId: Number(this.props.match.params.id),
     loading: true
   }
@@ -71,7 +74,11 @@ export class Community extends Component<any, State> {
             <PostListings communityId={this.state.communityId} />
           </div>
           <div class="col-12 col-md-3">
-            <Sidebar community={this.state.community} moderators={this.state.moderators} />
+            <Sidebar 
+              community={this.state.community} 
+              moderators={this.state.moderators} 
+              admins={this.state.admins}
+            />
           </div>
         </div>
         }
@@ -90,6 +97,7 @@ export class Community extends Component<any, State> {
       let res: GetCommunityResponse = msg;
       this.state.community = res.community;
       this.state.moderators = res.moderators;
+      this.state.admins = res.admins;
       this.state.loading = false;
       this.setState(this.state);
     } else if (op == UserOperation.EditCommunity) {
index 8803d6290217818abef75f4fcab8421f7bb6ce2f..93e88071eed4ceb0d8fbb53002f02b252fdf5124 100644 (file)
@@ -174,6 +174,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     return UserService.Instance.user && this.props.post.creator_id == UserService.Instance.user.id;
   }
 
+  get isMod(): boolean {
+    return this.props.moderators && isMod(this.props.moderators.map(m => m.user_id), this.props.post.creator_id);
+  }
+
+  get isAdmin(): boolean {
+    return this.props.admins && isMod(this.props.admins.map(a => a.id), this.props.post.creator_id);
+  }
+
   get canMod(): boolean {
 
     if (this.props.editable) {
@@ -185,18 +193,6 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     } else return false;
   }
 
-  get isMod(): boolean {
-    return this.props.moderators && isMod(this.props.moderators.map(m => m.user_id), this.props.post.creator_id);
-  }
-
-  get isAdmin(): boolean {
-    return this.props.admins && isMod(this.props.admins.map(a => a.id), this.props.post.creator_id);
-  }
-
-  get canAdmin(): boolean {
-    return this.props.admins && canMod(UserService.Instance.user, this.props.admins.map(a => a.id), this.props.post.creator_id);
-  }
-
   handlePostLike(i: PostListing) {
 
     let form: CreatePostLikeForm = {
index 3f243220a061ad3a108c1bd79972f1bd07be154f..3ece674795e903b4344f54f001b9440c22a098ea 100644 (file)
@@ -148,7 +148,11 @@ export class Post extends Component<any, PostState> {
   sidebar() {
     return ( 
       <div class="sticky-top">
-        <Sidebar community={this.state.community} moderators={this.state.moderators} />
+        <Sidebar 
+          community={this.state.community} 
+          moderators={this.state.moderators} 
+          admins={this.state.admins}
+        />
       </div>
     );
   }
index 2f231f9a09dcfcf44c11e11b11991f8435f9cd9b..958804484778d861ff5580e61bbb65c931ca3f9d 100644 (file)
@@ -1,6 +1,6 @@
 import { Component, linkEvent } from 'inferno';
 import { Link } from 'inferno-router';
-import { Community, CommunityUser, FollowCommunityForm, CommunityForm as CommunityFormI } from '../interfaces';
+import { Community, CommunityUser, FollowCommunityForm, CommunityForm as CommunityFormI, UserView } from '../interfaces';
 import { WebSocketService, UserService } from '../services';
 import { mdToHtml, getUnixTime } from '../utils';
 import { CommunityForm } from './community-form';
@@ -8,6 +8,7 @@ import { CommunityForm } from './community-form';
 interface SidebarProps {
   community: Community;
   moderators: Array<CommunityUser>;
+  admins: Array<UserView>;
 }
 
 interface SidebarState {
@@ -54,24 +55,29 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
         }
       </h5>
       <Link className="text-muted" to={`/community/${community.id}`}>/f/{community.name}</Link>
-      {community.am_mod && 
-        <ul class="list-inline mb-1 text-muted small font-weight-bold"> 
-          <li className="list-inline-item">
-            <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
-          </li>
-          {this.amCreator && 
+      <ul class="list-inline mb-1 text-muted small font-weight-bold"> 
+        {this.canMod && 
+          <>
             <li className="list-inline-item">
-              {/* <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span> */}
+              <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
             </li>
-          }
+            {this.amCreator && 
+              <li className="list-inline-item">
+                {/* <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span> */}
+              </li>
+            }
+          </>
+        }
+        {this.canAdmin &&
           <li className="list-inline-item">
             {!this.props.community.removed ? 
             <span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}>remove</span> :
             <span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}>restore</span>
             }
           </li>
-        </ul>
-      }
+
+        }
+      </ul>
       {this.state.showRemoveDialog && 
         <form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
           <div class="form-group row">
@@ -156,10 +162,13 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
     return this.props.community.creator_id == UserService.Instance.user.id;
   }
 
-  // private get amMod(): boolean {
-  //   return UserService.Instance.loggedIn && 
-  //     this.props.moderators.map(m => m.user_id).includes(UserService.Instance.user.id);
-  // }
+  get canMod(): boolean {
+    return UserService.Instance.user && this.props.moderators.map(m => m.user_id).includes(UserService.Instance.user.id);
+  }
+
+  get canAdmin(): boolean {
+    return UserService.Instance.user && this.props.admins.map(a => a.id).includes(UserService.Instance.user.id);
+  }
 
   handleDeleteClick() {
   }
index 24bb6157f55a4390b06605016d72f776aaa1d2de..23b86074520f058b682027b873ec900267aebb83 100644 (file)
@@ -72,6 +72,7 @@ export interface Post {
   updated?: string;
   creator_name: string;
   community_name: string;
+  community_removed: boolean;
   number_of_comments: number;
   score: number;
   upvotes: number;
@@ -350,6 +351,7 @@ export interface GetCommunityResponse {
   op: string;
   community: Community;
   moderators: Array<CommunityUser>;
+  admins: Array<UserView>;
 }