},
person::Person,
post::Post,
- site::Site,
},
traits::{Bannable, Blockable, Crud, Followable, Joinable},
};
let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
- let site_creator_id = blocking(context.pool(), move |conn| {
- Site::read(conn, 1).map(|s| s.creator_id)
- })
- .await??;
-
- let mut admins = blocking(context.pool(), PersonViewSafe::admins).await??;
-
- // Making sure the site creator, if an admin, is at the top
- let creator_index = admins
- .iter()
- .position(|r| r.person.id == site_creator_id)
- .context(location_info!())?;
- let creator_person = admins.remove(creator_index);
- admins.insert(0, creator_person);
+ let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
// Fetch the community mods
let community_id = data.community_id;
UserOperation::TransferCommunity => {
do_websocket_operation::<TransferCommunity>(context, id, op, data).await
}
- UserOperation::TransferSite => {
- do_websocket_operation::<TransferSite>(context, id, op, data).await
- }
+ UserOperation::LeaveAdmin => do_websocket_operation::<LeaveAdmin>(context, id, op, data).await,
// Community ops
UserOperation::FollowCommunity => {
use crate::{captcha_as_wav_base64, Perform};
use actix_web::web::Data;
-use anyhow::Context;
use bcrypt::verify;
use captcha::{gen, Difficulty};
use chrono::Duration;
};
use lemmy_utils::{
claims::Claims,
- location_info,
utils::{is_valid_display_name, is_valid_matrix_id, naive_from_unix},
ConnectionId,
LemmyError,
blocking(context.pool(), move |conn| ModAdd::create(conn, &form)).await??;
- let site_creator_id = blocking(context.pool(), move |conn| {
- Site::read(conn, 1).map(|s| s.creator_id)
- })
- .await??;
-
- let mut admins = blocking(context.pool(), PersonViewSafe::admins).await??;
- let creator_index = admins
- .iter()
- .position(|r| r.person.id == site_creator_id)
- .context(location_info!())?;
- let creator_person = admins.remove(creator_index);
- admins.insert(0, creator_person);
+ let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
let res = AddAdminResponse { admins };
use crate::Perform;
use actix_web::web::Data;
-use anyhow::Context;
use diesel::NotFound;
use lemmy_api_common::{
blocking,
source::{
local_user::{LocalUser, LocalUserForm},
moderator::*,
+ person::Person,
registration_application::{RegistrationApplication, RegistrationApplicationForm},
site::Site,
},
mod_sticky_post_view::ModStickyPostView,
mod_transfer_community_view::ModTransferCommunityView,
};
-use lemmy_utils::{location_info, settings::structs::Settings, version, ConnectionId, LemmyError};
+use lemmy_utils::{settings::structs::Settings, version, ConnectionId, LemmyError};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
}
#[async_trait::async_trait(?Send)]
-impl Perform for TransferSite {
+impl Perform for LeaveAdmin {
type Response = GetSiteResponse;
#[tracing::instrument(skip(context, _websocket_id))]
context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>,
) -> Result<GetSiteResponse, LemmyError> {
- let data: &TransferSite = self;
+ let data: &LeaveAdmin = self;
let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
is_admin(&local_user_view)?;
- let read_site = blocking(context.pool(), Site::read_simple).await??;
-
- // Make sure user is the creator
- if read_site.creator_id != local_user_view.person.id {
- return Err(LemmyError::from_message("not_an_admin"));
+ // Make sure there isn't just one admin (so if one leaves, there will still be one left)
+ let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
+ if admins.len() == 1 {
+ return Err(LemmyError::from_message("cannot_leave_admin"));
}
- let new_creator_id = data.person_id;
- let transfer_site = move |conn: &'_ _| Site::transfer(conn, new_creator_id);
- blocking(context.pool(), transfer_site)
- .await?
- .map_err(LemmyError::from)
- .map_err(|e| e.with_message("couldnt_update_site"))?;
+ let person_id = local_user_view.person.id;
+ blocking(context.pool(), move |conn| {
+ Person::leave_admin(conn, person_id)
+ })
+ .await??;
// Mod tables
let form = ModAddForm {
- mod_person_id: local_user_view.person.id,
- other_person_id: data.person_id,
- removed: Some(false),
+ mod_person_id: person_id,
+ other_person_id: person_id,
+ removed: Some(true),
};
blocking(context.pool(), move |conn| ModAdd::create(conn, &form)).await??;
+ // Reread site and admins
let site_view = blocking(context.pool(), SiteView::read).await??;
-
- let mut admins = blocking(context.pool(), PersonViewSafe::admins).await??;
- let creator_index = admins
- .iter()
- .position(|r| r.person.id == site_view.creator.id)
- .context(location_info!())?;
- let creator_person = admins.remove(creator_index);
- admins.insert(0, creator_person);
+ let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
let federated_instances = build_federated_instances(
context.pool(),
}
#[derive(Debug, Serialize, Deserialize)]
-pub struct TransferSite {
- pub person_id: PersonId,
+pub struct LeaveAdmin {
pub auth: Sensitive<String>,
}
description,
icon,
banner,
- creator_id: local_user_view.person.id,
enable_downvotes: data.enable_downvotes,
open_registration: data.open_registration,
enable_nsfw: data.enable_nsfw,
}
};
- let mut admins = blocking(context.pool(), PersonViewSafe::admins).await??;
-
- // Make sure the site creator is the top admin
- if let Some(site_view) = site_view.to_owned() {
- let site_creator_id = site_view.creator.id;
- // TODO investigate why this is sometimes coming back null
- // Maybe user_.admin isn't being set to true?
- if let Some(creator_index) = admins.iter().position(|r| r.person.id == site_creator_id) {
- let creator_person = admins.remove(creator_index);
- admins.insert(0, creator_person);
- }
- }
+ let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
let online = context
.chat_server()
}
let site_form = SiteForm {
- creator_id: found_site.creator_id,
name: data.name.to_owned().unwrap_or(found_site.name),
sidebar,
description,
let site_form = SiteForm {
name: "test_site".into(),
- creator_id: inserted_person.id,
sidebar: None,
description: None,
icon: None,
let community_num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
assert_eq!(1, community_num_deleted);
- let after_delete = SiteAggregates::read(&conn);
- assert!(after_delete.is_err());
+ // Site should still exist, it can without a site creator.
+ let after_delete_creator = SiteAggregates::read(&conn);
+ assert!(after_delete_creator.is_ok());
+
+ Site::delete(&conn, 1).unwrap();
+ let after_delete_site = SiteAggregates::read(&conn);
+ assert!(after_delete_site.is_err());
}
}
pub fn is_banned(&self) -> bool {
is_banned(self.banned, self.ban_expires)
}
+
+ pub fn leave_admin(conn: &PgConnection, person_id: PersonId) -> Result<Self, Error> {
+ diesel::update(person.find(person_id))
+ .set(admin.eq(false))
+ .get_result::<Self>(conn)
+ }
}
impl PersonSafe {
-use crate::{naive_now, newtypes::PersonId, source::site::*, traits::Crud};
+use crate::{source::site::*, traits::Crud};
use diesel::{dsl::*, result::Error, *};
impl Crud for Site {
}
impl Site {
- pub fn transfer(conn: &PgConnection, new_creator_id: PersonId) -> Result<Site, Error> {
- use crate::schema::site::dsl::*;
- diesel::update(site.find(1))
- .set((creator_id.eq(new_creator_id), updated.eq(naive_now())))
- .get_result::<Self>(conn)
- }
-
pub fn read_simple(conn: &PgConnection) -> Result<Self, Error> {
use crate::schema::site::dsl::*;
site.first::<Self>(conn)
id -> Int4,
name -> Varchar,
sidebar -> Nullable<Text>,
- creator_id -> Int4,
published -> Timestamp,
updated -> Nullable<Timestamp>,
enable_downvotes -> Bool,
joinable!(post_report -> post (post_id));
joinable!(post_saved -> person (person_id));
joinable!(post_saved -> post (post_id));
-joinable!(site -> person (creator_id));
joinable!(site_aggregates -> site (site_id));
joinable!(email_verification -> local_user (local_user_id));
joinable!(registration_application -> local_user (local_user_id));
-use crate::{
- newtypes::{DbUrl, PersonId},
- schema::site,
-};
+use crate::{newtypes::DbUrl, schema::site};
use serde::{Deserialize, Serialize};
#[derive(Queryable, Identifiable, PartialEq, Debug, Clone, Serialize, Deserialize)]
pub id: i32,
pub name: String,
pub sidebar: Option<String>,
- pub creator_id: PersonId,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub enable_downvotes: bool,
#[table_name = "site"]
pub struct SiteForm {
pub name: String,
- pub creator_id: PersonId,
pub sidebar: Option<Option<String>>,
pub updated: Option<chrono::NaiveDateTime>,
pub enable_downvotes: Option<bool>,
use diesel::{result::Error, *};
use lemmy_db_schema::{
aggregates::site_aggregates::SiteAggregates,
- schema::{person, site, site_aggregates},
- source::{
- person::{Person, PersonSafe},
- site::Site,
- },
- traits::ToSafe,
+ schema::{site, site_aggregates},
+ source::site::Site,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SiteView {
pub site: Site,
- pub creator: PersonSafe,
pub counts: SiteAggregates,
}
impl SiteView {
pub fn read(conn: &PgConnection) -> Result<Self, Error> {
- let (site, creator, counts) = site::table
- .inner_join(person::table)
+ let (site, counts) = site::table
.inner_join(site_aggregates::table)
- .select((
- site::all_columns,
- Person::safe_columns_tuple(),
- site_aggregates::all_columns,
- ))
- .first::<(Site, PersonSafe, SiteAggregates)>(conn)?;
+ .select((site::all_columns, site_aggregates::all_columns))
+ .first::<(Site, SiteAggregates)>(conn)?;
- Ok(SiteView {
- site,
- creator,
- counts,
- })
+ Ok(SiteView { site, counts })
}
}
MarkAllAsRead,
SaveUserSettings,
TransferCommunity,
- TransferSite,
+ LeaveAdmin,
PasswordReset,
PasswordChange,
MarkPrivateMessageAsRead,
--- /dev/null
+-- Add the column back
+alter table site add column creator_id int references person on update cascade on delete cascade;
+
+-- Add the data, selecting the highest admin
+update site
+set creator_id = sub.id
+from (
+ select id from person
+ where admin = true
+ limit 1
+) as sub;
+
+-- Set to not null
+alter table site alter column creator_id set not null;
--- /dev/null
+-- Drop the column
+alter table site drop column creator_id;
// Admin Actions
.route("", web::post().to(route_post_crud::<CreateSite>))
.route("", web::put().to(route_post_crud::<EditSite>))
- .route("/transfer", web::post().to(route_post::<TransferSite>))
.route("/config", web::get().to(route_get::<GetSiteConfig>))
.route("/config", web::put().to(route_post::<SaveSiteConfig>)),
)
)
.route("/report_count", web::get().to(route_get::<GetReportCount>))
.route("/unread_count", web::get().to(route_get::<GetUnreadCount>))
- .route("/verify_email", web::post().to(route_post::<VerifyEmail>)),
+ .route("/verify_email", web::post().to(route_post::<VerifyEmail>))
+ .route("/leave_admin", web::post().to(route_post::<LeaveAdmin>)),
)
// Admin Actions
.service(