Merge branch 'main' into drone-release-main

drone-release-main
Felix Ableitner 3 years ago
commit 3a6e0d9393

@ -1,3 +1,10 @@
# Lemmy v0.10.3 Release (2021-04-07)
- Fixing instances page.
- Fixed unban not working.
- Fixed post title fetching and cross-post search.
- Fixed navigating to a user page.
# Lemmy v0.10.2 Release (2021-04-05) # Lemmy v0.10.2 Release (2021-04-05)
- Forcing a crash if config.hjson fails to load. Should show errors easier. - Forcing a crash if config.hjson fails to load. Should show errors easier.

@ -1 +1 @@
0.10.2 0.10.3

@ -16,7 +16,7 @@
"eslint": "^7.18.0", "eslint": "^7.18.0",
"eslint-plugin-jane": "^9.0.3", "eslint-plugin-jane": "^9.0.3",
"jest": "^26.6.3", "jest": "^26.6.3",
"lemmy-js-client": "0.10.0-rc.13", "lemmy-js-client": "0.11.0-rc.3",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"prettier": "^2.1.2", "prettier": "^2.1.2",
"ts-jest": "^26.4.4", "ts-jest": "^26.4.4",

@ -31,7 +31,6 @@ function assertCommunityFederation(
expect(communityOne.community.published).toBe( expect(communityOne.community.published).toBe(
communityTwo.community.published communityTwo.community.published
); );
expect(communityOne.creator.actor_id).toBe(communityTwo.creator.actor_id);
expect(communityOne.community.nsfw).toBe(communityTwo.community.nsfw); expect(communityOne.community.nsfw).toBe(communityTwo.community.nsfw);
expect(communityOne.community.removed).toBe(communityTwo.community.removed); expect(communityOne.community.removed).toBe(communityTwo.community.removed);
expect(communityOne.community.deleted).toBe(communityTwo.community.deleted); expect(communityOne.community.deleted).toBe(communityTwo.community.deleted);

@ -15,8 +15,8 @@ let recipient_id: number;
beforeAll(async () => { beforeAll(async () => {
await setupLogins(); await setupLogins();
let follow = await followBeta(alpha); await followBeta(alpha);
recipient_id = follow.community_view.creator.id; recipient_id = 3;
}); });
afterAll(async () => { afterAll(async () => {

@ -3233,10 +3233,10 @@ language-tags@^1.0.5:
dependencies: dependencies:
language-subtag-registry "~0.3.2" language-subtag-registry "~0.3.2"
lemmy-js-client@0.10.0-rc.13: lemmy-js-client@0.11.0-rc.3:
version "0.10.0-rc.13" version "0.11.0-rc.3"
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.10.0-rc.13.tgz#ea2e88857243374d7fbd49ee6b4bb94c34359d85" resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.11.0-rc.3.tgz#dd4727ca4d16fe9593368725aacfd9e7a8d52548"
integrity sha512-zodvYkwBYR7iP27ah6L/QPUphUUdq38kCH7QF2CUYBrsSAEkGmq2kdz+iusnQ1Ht7Ad80GtYycFprsZBveV5eQ== integrity sha512-16mgl+TS1+0UHiY+ZKPuqHfbrn93h8K8tJ+kKJ1pjT2WhG23o0B8dLahG1jDQPG+dkXpR6PZxYudMYjuO8WvjQ==
leven@^3.1.0: leven@^3.1.0:
version "3.1.0" version "3.1.0"

@ -10,11 +10,7 @@ use lemmy_api_common::{
}; };
use lemmy_apub::{ActorType, CommunityType, UserType}; use lemmy_apub::{ActorType, CommunityType, UserType};
use lemmy_db_queries::{ use lemmy_db_queries::{
source::{ source::{comment::Comment_, community::CommunityModerator_, post::Post_},
comment::Comment_,
community::{CommunityModerator_, Community_},
post::Post_,
},
Bannable, Bannable,
Crud, Crud,
Followable, Followable,
@ -324,12 +320,6 @@ impl Perform for TransferCommunity {
let data: &TransferCommunity = &self; let data: &TransferCommunity = &self;
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
let community_id = data.community_id;
let read_community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id)
})
.await??;
let site_creator_id = blocking(context.pool(), move |conn| { let site_creator_id = blocking(context.pool(), move |conn| {
Site::read(conn, 1).map(|s| s.creator_id) Site::read(conn, 1).map(|s| s.creator_id)
}) })
@ -337,7 +327,7 @@ impl Perform for TransferCommunity {
let mut admins = blocking(context.pool(), move |conn| PersonViewSafe::admins(conn)).await??; let mut admins = blocking(context.pool(), move |conn| PersonViewSafe::admins(conn)).await??;
// Making sure the creator, if an admin, is at the top // Making sure the site creator, if an admin, is at the top
let creator_index = admins let creator_index = admins
.iter() .iter()
.position(|r| r.person.id == site_creator_id) .position(|r| r.person.id == site_creator_id)
@ -345,8 +335,15 @@ impl Perform for TransferCommunity {
let creator_person = admins.remove(creator_index); let creator_person = admins.remove(creator_index);
admins.insert(0, creator_person); admins.insert(0, creator_person);
// Make sure user is the creator, or an admin // Fetch the community mods
if local_user_view.person.id != read_community.creator_id let community_id = data.community_id;
let mut community_mods = blocking(context.pool(), move |conn| {
CommunityModeratorView::for_community(conn, community_id)
})
.await??;
// Make sure transferrer is either the top community mod, or an admin
if local_user_view.person.id != community_mods[0].moderator.id
&& !admins && !admins
.iter() .iter()
.map(|a| a.person.id) .map(|a| a.person.id)
@ -355,19 +352,8 @@ impl Perform for TransferCommunity {
return Err(ApiError::err("not_an_admin").into()); return Err(ApiError::err("not_an_admin").into());
} }
let community_id = data.community_id; // You have to re-do the community_moderator table, reordering it.
let new_creator = data.person_id; // Add the transferee to the top
let update = move |conn: &'_ _| Community::update_creator(conn, community_id, new_creator);
if blocking(context.pool(), update).await?.is_err() {
return Err(ApiError::err("couldnt_update_community").into());
};
// You also have to re-do the community_moderator table, reordering it.
let community_id = data.community_id;
let mut community_mods = blocking(context.pool(), move |conn| {
CommunityModeratorView::for_community(conn, community_id)
})
.await??;
let creator_index = community_mods let creator_index = community_mods
.iter() .iter()
.position(|r| r.moderator.id == data.person_id) .position(|r| r.moderator.id == data.person_id)
@ -375,6 +361,7 @@ impl Perform for TransferCommunity {
let creator_person = community_mods.remove(creator_index); let creator_person = community_mods.remove(creator_index);
community_mods.insert(0, creator_person); community_mods.insert(0, creator_person);
// Delete all the mods
let community_id = data.community_id; let community_id = data.community_id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
CommunityModerator::delete_for_community(conn, community_id) CommunityModerator::delete_for_community(conn, community_id)
@ -382,6 +369,7 @@ impl Perform for TransferCommunity {
.await??; .await??;
// TODO: this should probably be a bulk operation // TODO: this should probably be a bulk operation
// Re-add the mods, in the new order
for cmod in &community_mods { for cmod in &community_mods {
let community_moderator_form = CommunityModeratorForm { let community_moderator_form = CommunityModeratorForm {
community_id: cmod.community.id, community_id: cmod.community.id,
@ -395,6 +383,8 @@ impl Perform for TransferCommunity {
} }
// Mod tables // Mod tables
// TODO there should probably be another table for transfer community
// Right now, it will just look like it modded them twice
let form = ModAddCommunityForm { let form = ModAddCommunityForm {
mod_person_id: local_user_view.person.id, mod_person_id: local_user_view.person.id,
other_person_id: data.person_id, other_person_id: data.person_id,

@ -1,9 +1,9 @@
use actix_web::{web, web::Data}; use actix_web::{web, web::Data};
use captcha::Captcha;
use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*, websocket::*}; use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*, websocket::*};
use lemmy_utils::{ConnectionId, LemmyError}; use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation}; use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
use serde::Deserialize; use serde::Deserialize;
use std::{env, process::Command};
mod comment; mod comment;
mod comment_report; mod comment_report;
@ -63,6 +63,9 @@ pub async fn match_websocket_operation(
UserOperation::SaveUserSettings => { UserOperation::SaveUserSettings => {
do_websocket_operation::<SaveUserSettings>(context, id, op, data).await do_websocket_operation::<SaveUserSettings>(context, id, op, data).await
} }
UserOperation::ChangePassword => {
do_websocket_operation::<ChangePassword>(context, id, op, data).await
}
UserOperation::GetReportCount => { UserOperation::GetReportCount => {
do_websocket_operation::<GetReportCount>(context, id, op, data).await do_websocket_operation::<GetReportCount>(context, id, op, data).await
} }
@ -158,60 +161,23 @@ where
serialize_websocket_message(&op, &res) serialize_websocket_message(&op, &res)
} }
pub(crate) fn captcha_espeak_wav_base64(captcha: &str) -> Result<String, LemmyError> { /// Converts the captcha to a base64 encoded wav audio file
let mut built_text = String::new(); pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String {
let letters = captcha.as_wav();
// Building proper speech text for espeak let mut concat_letters: Vec<u8> = Vec::new();
for mut c in captcha.chars() {
let new_str = if c.is_alphabetic() {
if c.is_lowercase() {
c.make_ascii_uppercase();
format!("lower case {} ... ", c)
} else {
c.make_ascii_uppercase();
format!("capital {} ... ", c)
}
} else {
format!("{} ...", c)
};
built_text.push_str(&new_str); for letter in letters {
let bytes = letter.unwrap_or_default();
concat_letters.extend(bytes);
} }
espeak_wav_base64(&built_text)
}
pub(crate) fn espeak_wav_base64(text: &str) -> Result<String, LemmyError> {
// Make a temp file path
let uuid = uuid::Uuid::new_v4().to_string();
let file_path = format!(
"{}/lemmy_espeak_{}.wav",
env::temp_dir().to_string_lossy(),
&uuid
);
// Write the wav file
Command::new("espeak")
.arg("-w")
.arg(&file_path)
.arg(text)
.status()?;
// Read the wav file bytes
let bytes = std::fs::read(&file_path)?;
// Delete the file
std::fs::remove_file(file_path)?;
// Convert to base64 // Convert to base64
let base64 = base64::encode(bytes); base64::encode(concat_letters)
Ok(base64)
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::captcha_espeak_wav_base64;
use lemmy_api_common::check_validator_time; use lemmy_api_common::check_validator_time;
use lemmy_db_queries::{establish_unpooled_connection, source::local_user::LocalUser_, Crud}; use lemmy_db_queries::{establish_unpooled_connection, source::local_user::LocalUser_, Crud};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
@ -253,9 +219,4 @@ mod tests {
let num_deleted = Person::delete(&conn, inserted_person.id).unwrap(); let num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
assert_eq!(1, num_deleted); assert_eq!(1, num_deleted);
} }
#[test]
fn test_espeak() {
assert!(captcha_espeak_wav_base64("WxRt2l").is_ok())
}
} }

@ -1,4 +1,4 @@
use crate::{captcha_espeak_wav_base64, Perform}; use crate::{captcha_as_wav_base64, Perform};
use actix_web::web::Data; use actix_web::web::Data;
use anyhow::Context; use anyhow::Context;
use bcrypt::verify; use bcrypt::verify;
@ -18,7 +18,6 @@ use lemmy_db_queries::{
diesel_option_overwrite_to_url, diesel_option_overwrite_to_url,
source::{ source::{
comment::Comment_, comment::Comment_,
community::Community_,
local_user::LocalUser_, local_user::LocalUser_,
password_reset_request::PasswordResetRequest_, password_reset_request::PasswordResetRequest_,
person::Person_, person::Person_,
@ -33,7 +32,6 @@ use lemmy_db_schema::{
naive_now, naive_now,
source::{ source::{
comment::Comment, comment::Comment,
community::*,
local_user::{LocalUser, LocalUserForm}, local_user::{LocalUser, LocalUserForm},
moderator::*, moderator::*,
password_reset_request::*, password_reset_request::*,
@ -60,7 +58,7 @@ use lemmy_utils::{
email::send_email, email::send_email,
location_info, location_info,
settings::structs::Settings, settings::structs::Settings,
utils::{generate_random_string, is_valid_preferred_username, naive_from_unix}, utils::{generate_random_string, is_valid_display_name, is_valid_matrix_id, naive_from_unix},
ApiError, ApiError,
ConnectionId, ConnectionId,
LemmyError, LemmyError,
@ -135,13 +133,11 @@ impl Perform for GetCaptcha {
let answer = captcha.chars_as_string(); let answer = captcha.chars_as_string();
let png_byte_array = captcha.as_png().expect("failed to generate captcha"); let png = captcha.as_base64().expect("failed to generate captcha");
let png = base64::encode(png_byte_array);
let uuid = uuid::Uuid::new_v4().to_string(); let uuid = uuid::Uuid::new_v4().to_string();
let wav = captcha_espeak_wav_base64(&answer).ok(); let wav = captcha_as_wav_base64(&captcha);
let captcha_item = CaptchaItem { let captcha_item = CaptchaItem {
answer, answer,
@ -174,7 +170,7 @@ impl Perform for SaveUserSettings {
let banner = diesel_option_overwrite_to_url(&data.banner)?; let banner = diesel_option_overwrite_to_url(&data.banner)?;
let email = diesel_option_overwrite(&data.email); let email = diesel_option_overwrite(&data.email);
let bio = diesel_option_overwrite(&data.bio); let bio = diesel_option_overwrite(&data.bio);
let preferred_username = diesel_option_overwrite(&data.preferred_username); let display_name = diesel_option_overwrite(&data.display_name);
let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id); let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id);
if let Some(Some(bio)) = &bio { if let Some(Some(bio)) = &bio {
@ -183,59 +179,30 @@ impl Perform for SaveUserSettings {
} }
} }
if let Some(Some(preferred_username)) = &preferred_username { if let Some(Some(display_name)) = &display_name {
if !is_valid_preferred_username(preferred_username.trim()) { if !is_valid_display_name(display_name.trim()) {
return Err(ApiError::err("invalid_username").into()); return Err(ApiError::err("invalid_username").into());
} }
} }
let local_user_id = local_user_view.local_user.id; if let Some(Some(matrix_user_id)) = &matrix_user_id {
let person_id = local_user_view.person.id; if !is_valid_matrix_id(matrix_user_id) {
let password_encrypted = match &data.new_password { return Err(ApiError::err("invalid_matrix_id").into());
Some(new_password) => {
match &data.new_password_verify {
Some(new_password_verify) => {
password_length_check(&new_password)?;
// Make sure passwords match
if new_password != new_password_verify {
return Err(ApiError::err("passwords_dont_match").into());
}
// Check the old password
match &data.old_password {
Some(old_password) => {
let valid: bool =
verify(old_password, &local_user_view.local_user.password_encrypted)
.unwrap_or(false);
if !valid {
return Err(ApiError::err("password_incorrect").into());
}
let new_password = new_password.to_owned();
let user = blocking(context.pool(), move |conn| {
LocalUser::update_password(conn, local_user_id, &new_password)
})
.await??;
user.password_encrypted
}
None => return Err(ApiError::err("password_incorrect").into()),
}
}
None => return Err(ApiError::err("passwords_dont_match").into()),
}
} }
None => local_user_view.local_user.password_encrypted, }
};
let local_user_id = local_user_view.local_user.id;
let person_id = local_user_view.person.id;
let default_listing_type = data.default_listing_type; let default_listing_type = data.default_listing_type;
let default_sort_type = data.default_sort_type; let default_sort_type = data.default_sort_type;
let password_encrypted = local_user_view.local_user.password_encrypted;
let person_form = PersonForm { let person_form = PersonForm {
name: local_user_view.person.name, name: local_user_view.person.name,
avatar, avatar,
banner, banner,
inbox_url: None, inbox_url: None,
preferred_username, display_name,
published: None, published: None,
updated: Some(naive_now()), updated: Some(naive_now()),
banned: None, banned: None,
@ -267,6 +234,7 @@ impl Perform for SaveUserSettings {
email, email,
password_encrypted, password_encrypted,
show_nsfw: data.show_nsfw, show_nsfw: data.show_nsfw,
show_scores: data.show_scores,
theme: data.theme.to_owned(), theme: data.theme.to_owned(),
default_sort_type, default_sort_type,
default_listing_type, default_listing_type,
@ -301,6 +269,49 @@ impl Perform for SaveUserSettings {
} }
} }
#[async_trait::async_trait(?Send)]
impl Perform for ChangePassword {
type Response = LoginResponse;
async fn perform(
&self,
context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>,
) -> Result<LoginResponse, LemmyError> {
let data: &ChangePassword = &self;
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
password_length_check(&data.new_password)?;
// Make sure passwords match
if data.new_password != data.new_password_verify {
return Err(ApiError::err("passwords_dont_match").into());
}
// Check the old password
let valid: bool = verify(
&data.old_password,
&local_user_view.local_user.password_encrypted,
)
.unwrap_or(false);
if !valid {
return Err(ApiError::err("password_incorrect").into());
}
let local_user_id = local_user_view.local_user.id;
let new_password = data.new_password.to_owned();
let updated_local_user = blocking(context.pool(), move |conn| {
LocalUser::update_password(conn, local_user_id, &new_password)
})
.await??;
// Return the jwt
Ok(LoginResponse {
jwt: Claims::jwt(updated_local_user.id.0)?,
})
}
}
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for AddAdmin { impl Perform for AddAdmin {
type Response = AddAdminResponse; type Response = AddAdminResponse;
@ -394,10 +405,9 @@ impl Perform for BanPerson {
.await??; .await??;
// Communities // Communities
blocking(context.pool(), move |conn: &'_ _| { // Remove all communities where they're the top mod
Community::update_removed_for_creator(conn, banned_person_id, true) // TODO couldn't get group by's working in diesel,
}) // for now, remove the communities manually
.await??;
// Comments // Comments
blocking(context.pool(), move |conn: &'_ _| { blocking(context.pool(), move |conn: &'_ _| {

@ -418,3 +418,12 @@ pub fn password_length_check(pass: &str) -> Result<(), LemmyError> {
Ok(()) Ok(())
} }
} }
/// Checks the site description length
pub fn site_description_length_check(description: &str) -> Result<(), LemmyError> {
if description.len() > 150 {
Err(ApiError::err("site_description_length_overflow").into())
} else {
Ok(())
}
}

@ -39,32 +39,41 @@ pub struct GetCaptchaResponse {
#[derive(Serialize)] #[derive(Serialize)]
pub struct CaptchaResponse { pub struct CaptchaResponse {
pub png: String, // A Base64 encoded png pub png: String, // A Base64 encoded png
pub wav: Option<String>, // A Base64 encoded wav audio pub wav: String, // A Base64 encoded wav audio
pub uuid: String, pub uuid: String,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct SaveUserSettings { pub struct SaveUserSettings {
pub show_nsfw: Option<bool>, pub show_nsfw: Option<bool>,
pub show_scores: Option<bool>,
pub theme: Option<String>, pub theme: Option<String>,
pub default_sort_type: Option<i16>, pub default_sort_type: Option<i16>,
pub default_listing_type: Option<i16>, pub default_listing_type: Option<i16>,
pub lang: Option<String>, pub lang: Option<String>,
pub avatar: Option<String>, pub avatar: Option<String>,
pub banner: Option<String>, pub banner: Option<String>,
pub preferred_username: Option<String>, pub display_name: Option<String>,
pub email: Option<String>, pub email: Option<String>,
pub bio: Option<String>, pub bio: Option<String>,
pub matrix_user_id: Option<String>, pub matrix_user_id: Option<String>,
pub show_avatars: Option<bool>,
pub new_password: Option<String>, pub new_password: Option<String>,
pub new_password_verify: Option<String>, pub new_password_verify: Option<String>,
pub old_password: Option<String>, pub old_password: Option<String>,
pub show_avatars: Option<bool>,
pub send_notifications_to_email: Option<bool>, pub send_notifications_to_email: Option<bool>,
pub auth: String, pub auth: String,
} }
#[derive(Deserialize)]
pub struct ChangePassword {
pub new_password: String,
pub new_password_verify: String,
pub old_password: String,
pub auth: String,
}
#[derive(Serialize)] #[derive(Serialize)]
pub struct LoginResponse { pub struct LoginResponse {
pub jwt: String, pub jwt: String,

@ -18,7 +18,6 @@ use lemmy_db_views_moderator::{
mod_sticky_post_view::ModStickyPostView, mod_sticky_post_view::ModStickyPostView,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct Search { pub struct Search {
@ -65,9 +64,10 @@ pub struct GetModlogResponse {
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct CreateSite { pub struct CreateSite {
pub name: String, pub name: String,
pub sidebar: Option<String>,
pub description: Option<String>, pub description: Option<String>,
pub icon: Option<Url>, pub icon: Option<String>,
pub banner: Option<Url>, pub banner: Option<String>,
pub enable_downvotes: bool, pub enable_downvotes: bool,
pub open_registration: bool, pub open_registration: bool,
pub enable_nsfw: bool, pub enable_nsfw: bool,
@ -77,6 +77,7 @@ pub struct CreateSite {
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct EditSite { pub struct EditSite {
pub name: String, pub name: String,
pub sidebar: Option<String>,
pub description: Option<String>, pub description: Option<String>,
pub icon: Option<String>, pub icon: Option<String>,
pub banner: Option<String>, pub banner: Option<String>,

@ -75,7 +75,6 @@ impl PerformCrud for CreateCommunity {
description: data.description.to_owned(), description: data.description.to_owned(),
icon, icon,
banner, banner,
creator_id: local_user_view.person.id,
nsfw: data.nsfw, nsfw: data.nsfw,
actor_id: Some(community_actor_id.to_owned()), actor_id: Some(community_actor_id.to_owned()),
private_key: Some(keypair.private_key), private_key: Some(keypair.private_key),

@ -7,7 +7,10 @@ use lemmy_db_schema::source::{
community::*, community::*,
moderator::{ModRemoveCommunity, ModRemoveCommunityForm}, moderator::{ModRemoveCommunity, ModRemoveCommunityForm},
}; };
use lemmy_db_views_actor::community_view::CommunityView; use lemmy_db_views_actor::{
community_moderator_view::CommunityModeratorView,
community_view::CommunityView,
};
use lemmy_utils::{utils::naive_from_unix, ApiError, ConnectionId, LemmyError}; use lemmy_utils::{utils::naive_from_unix, ApiError, ConnectionId, LemmyError};
use lemmy_websocket::{LemmyContext, UserOperationCrud}; use lemmy_websocket::{LemmyContext, UserOperationCrud};
@ -23,13 +26,15 @@ impl PerformCrud for DeleteCommunity {
let data: &DeleteCommunity = &self; let data: &DeleteCommunity = &self;
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
// Verify its the creator (only a creator can delete the community) // Fetch the community mods
let community_id = data.community_id; let community_id = data.community_id;
let read_community = blocking(context.pool(), move |conn| { let community_mods = blocking(context.pool(), move |conn| {
Community::read(conn, community_id) CommunityModeratorView::for_community(conn, community_id)
}) })
.await??; .await??;
if read_community.creator_id != local_user_view.person.id {
// Make sure deleter is the top mod
if local_user_view.person.id != community_mods[0].moderator.id {
return Err(ApiError::err("no_community_edit_allowed").into()); return Err(ApiError::err("no_community_edit_allowed").into());
} }

@ -61,7 +61,6 @@ impl PerformCrud for EditCommunity {
let community_form = CommunityForm { let community_form = CommunityForm {
name: read_community.name, name: read_community.name,
title: data.title.to_owned(), title: data.title.to_owned(),
creator_id: read_community.creator_id,
description: data.description.to_owned(), description: data.description.to_owned(),
icon, icon,
banner, banner,

@ -1,7 +1,18 @@
use crate::PerformCrud; use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{blocking, get_local_user_view_from_jwt, is_admin, site::*}; use lemmy_api_common::{
use lemmy_db_queries::{source::site::Site_, Crud}; blocking,
get_local_user_view_from_jwt,
is_admin,
site::*,
site_description_length_check,
};
use lemmy_db_queries::{
diesel_option_overwrite,
diesel_option_overwrite_to_url,
source::site::Site_,
Crud,
};
use lemmy_db_schema::source::site::{Site, *}; use lemmy_db_schema::source::site::{Site, *};
use lemmy_db_views::site_view::SiteView; use lemmy_db_views::site_view::SiteView;
use lemmy_utils::{ use lemmy_utils::{
@ -36,11 +47,21 @@ impl PerformCrud for CreateSite {
// Make sure user is an admin // Make sure user is an admin
is_admin(&local_user_view)?; is_admin(&local_user_view)?;
let sidebar = diesel_option_overwrite(&data.sidebar);
let description = diesel_option_overwrite(&data.description);
let icon = diesel_option_overwrite_to_url(&data.icon)?;
let banner = diesel_option_overwrite_to_url(&data.banner)?;
if let Some(Some(desc)) = &description {
site_description_length_check(desc)?;
}
let site_form = SiteForm { let site_form = SiteForm {
name: data.name.to_owned(), name: data.name.to_owned(),
description: data.description.to_owned(), sidebar,
icon: Some(data.icon.to_owned().map(|url| url.into())), description,
banner: Some(data.banner.to_owned().map(|url| url.into())), icon,
banner,
creator_id: local_user_view.person.id, creator_id: local_user_view.person.id,
enable_downvotes: data.enable_downvotes, enable_downvotes: data.enable_downvotes,
open_registration: data.open_registration, open_registration: data.open_registration,

@ -43,6 +43,7 @@ impl PerformCrud for GetSite {
let create_site = CreateSite { let create_site = CreateSite {
name: setup.site_name.to_owned(), name: setup.site_name.to_owned(),
sidebar: None,
description: None, description: None,
icon: None, icon: None,
banner: None, banner: None,

@ -5,8 +5,14 @@ use lemmy_api_common::{
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
is_admin, is_admin,
site::{EditSite, SiteResponse}, site::{EditSite, SiteResponse},
site_description_length_check,
};
use lemmy_db_queries::{
diesel_option_overwrite,
diesel_option_overwrite_to_url,
source::site::Site_,
Crud,
}; };
use lemmy_db_queries::{diesel_option_overwrite_to_url, source::site::Site_, Crud};
use lemmy_db_schema::{ use lemmy_db_schema::{
naive_now, naive_now,
source::site::{Site, SiteForm}, source::site::{Site, SiteForm},
@ -39,12 +45,19 @@ impl PerformCrud for EditSite {
let found_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??; let found_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??;
let sidebar = diesel_option_overwrite(&data.sidebar);
let description = diesel_option_overwrite(&data.description);
let icon = diesel_option_overwrite_to_url(&data.icon)?; let icon = diesel_option_overwrite_to_url(&data.icon)?;
let banner = diesel_option_overwrite_to_url(&data.banner)?; let banner = diesel_option_overwrite_to_url(&data.banner)?;
if let Some(Some(desc)) = &description {
site_description_length_check(desc)?;
}
let site_form = SiteForm { let site_form = SiteForm {
name: data.name.to_owned(), name: data.name.to_owned(),
description: data.description.to_owned(), sidebar,
description,
icon, icon,
banner, banner,
creator_id: found_site.creator_id, creator_id: found_site.creator_id,

@ -133,6 +133,7 @@ impl PerformCrud for Register {
default_listing_type: Some(ListingType::Subscribed as i16), default_listing_type: Some(ListingType::Subscribed as i16),
lang: Some("browser".into()), lang: Some("browser".into()),
show_avatars: Some(true), show_avatars: Some(true),
show_scores: Some(true),
send_notifications_to_email: Some(false), send_notifications_to_email: Some(false),
}; };
@ -177,7 +178,6 @@ impl PerformCrud for Register {
name: default_community_name.to_string(), name: default_community_name.to_string(),
title: "The Default Community".to_string(), title: "The Default Community".to_string(),
description: Some("The Default Community".to_string()), description: Some("The Default Community".to_string()),
creator_id: inserted_person.id,
actor_id: Some(actor_id.to_owned()), actor_id: Some(actor_id.to_owned()),
private_key: Some(main_community_keypair.private_key), private_key: Some(main_community_keypair.private_key),
public_key: Some(main_community_keypair.public_key), public_key: Some(main_community_keypair.public_key),

@ -1,6 +1,6 @@
use crate::{ use crate::{
extensions::{context::lemmy_context, group_extension::GroupExtension}, extensions::{context::lemmy_context, group_extension::GroupExtension},
fetcher::{community::fetch_community_mods, person::get_or_fetch_and_upsert_person}, fetcher::community::fetch_community_mods,
generate_moderators_url, generate_moderators_url,
objects::{ objects::{
check_object_domain, check_object_domain,
@ -140,24 +140,7 @@ impl FromApubToForm<GroupExt> for CommunityForm {
request_counter: &mut i32, request_counter: &mut i32,
_mod_action_allowed: bool, _mod_action_allowed: bool,
) -> Result<Self, LemmyError> { ) -> Result<Self, LemmyError> {
let moderator_uris = fetch_community_mods(context, group, request_counter).await?; fetch_community_mods(context, group, request_counter).await?;
let creator = if let Some(creator_uri) = moderator_uris.first() {
get_or_fetch_and_upsert_person(creator_uri, context, request_counter)
} else {
// NOTE: code for compatibility with lemmy v0.9.9
let creator_uri = group
.inner
.attributed_to()
.map(|a| a.as_many())
.flatten()
.map(|a| a.first())
.flatten()
.map(|a| a.as_xsd_any_uri())
.flatten()
.context(location_info!())?;
get_or_fetch_and_upsert_person(creator_uri, context, request_counter)
}
.await?;
let name = group let name = group
.inner .inner
@ -215,7 +198,6 @@ impl FromApubToForm<GroupExt> for CommunityForm {
name, name,
title, title,
description, description,
creator_id: creator.id,
removed: None, removed: None,
published: group.inner.published().map(|u| u.to_owned().naive_local()), published: group.inner.published().map(|u| u.to_owned().naive_local()),
updated: group.inner.updated().map(|u| u.to_owned().naive_local()), updated: group.inner.updated().map(|u| u.to_owned().naive_local()),

@ -64,7 +64,8 @@ impl ToApub for DbPerson {
set_content_and_source(&mut person, bio)?; set_content_and_source(&mut person, bio)?;
} }
if let Some(i) = self.preferred_username.to_owned() { // In apub, the "name" is a display name
if let Some(i) = self.display_name.to_owned() {
person.set_name(i); person.set_name(i);
} }
@ -161,7 +162,7 @@ impl FromApubToForm<PersonExt> for PersonForm {
.preferred_username() .preferred_username()
.context(location_info!())? .context(location_info!())?
.to_string(); .to_string();
let preferred_username: Option<String> = person let display_name: Option<String> = person
.name() .name()
.map(|n| n.one()) .map(|n| n.one())
.flatten() .flatten()
@ -176,12 +177,12 @@ impl FromApubToForm<PersonExt> for PersonForm {
.map(|s| s.to_owned().into()); .map(|s| s.to_owned().into());
check_slurs(&name)?; check_slurs(&name)?;
check_slurs_opt(&preferred_username)?; check_slurs_opt(&display_name)?;
check_slurs_opt(&bio)?; check_slurs_opt(&bio)?;
Ok(PersonForm { Ok(PersonForm {
name, name,
preferred_username: Some(preferred_username), display_name: Some(display_name),
banned: None, banned: None,
deleted: None, deleted: None,
avatar: avatar.map(|o| o.map(|i| i.into())), avatar: avatar.map(|o| o.map(|i| i.into())),

@ -58,7 +58,6 @@ mod tests {
let new_community = CommunityForm { let new_community = CommunityForm {
name: "TIL_comment_agg".into(), name: "TIL_comment_agg".into(),
creator_id: inserted_person.id,
title: "nada".to_owned(), title: "nada".to_owned(),
..CommunityForm::default() ..CommunityForm::default()
}; };
@ -142,5 +141,9 @@ mod tests {
Person::delete(&conn, another_inserted_person.id).unwrap(); Person::delete(&conn, another_inserted_person.id).unwrap();
let person_num_deleted = Person::delete(&conn, inserted_person.id).unwrap(); let person_num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
assert_eq!(1, person_num_deleted); assert_eq!(1, person_num_deleted);
// Delete the community
let community_num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
assert_eq!(1, community_num_deleted);
} }
} }

@ -62,7 +62,6 @@ mod tests {
let new_community = CommunityForm { let new_community = CommunityForm {
name: "TIL_community_agg".into(), name: "TIL_community_agg".into(),
creator_id: inserted_person.id,
title: "nada".to_owned(), title: "nada".to_owned(),
..CommunityForm::default() ..CommunityForm::default()
}; };
@ -71,7 +70,6 @@ mod tests {
let another_community = CommunityForm { let another_community = CommunityForm {
name: "TIL_community_agg_2".into(), name: "TIL_community_agg_2".into(),
creator_id: inserted_person.id,
title: "nada".to_owned(), title: "nada".to_owned(),
..CommunityForm::default() ..CommunityForm::default()
}; };
@ -169,6 +167,14 @@ mod tests {
let person_num_deleted = Person::delete(&conn, inserted_person.id).unwrap(); let person_num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
assert_eq!(1, person_num_deleted); assert_eq!(1, person_num_deleted);
// Delete the community
let community_num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
assert_eq!(1, community_num_deleted);
let another_community_num_deleted =
Community::delete(&conn, another_inserted_community.id).unwrap();
assert_eq!(1, another_community_num_deleted);
// Should be none found, since the creator was deleted // Should be none found, since the creator was deleted
let after_delete = CommunityAggregates::read(&conn, inserted_community.id); let after_delete = CommunityAggregates::read(&conn, inserted_community.id);
assert!(after_delete.is_err()); assert!(after_delete.is_err());

@ -58,7 +58,6 @@ mod tests {
let new_community = CommunityForm { let new_community = CommunityForm {
name: "TIL_site_agg".into(), name: "TIL_site_agg".into(),
creator_id: inserted_person.id,
title: "nada".to_owned(), title: "nada".to_owned(),
..CommunityForm::default() ..CommunityForm::default()
}; };
@ -160,6 +159,10 @@ mod tests {
assert_eq!(1, person_num_deleted); assert_eq!(1, person_num_deleted);
Person::delete(&conn, another_inserted_person.id).unwrap(); Person::delete(&conn, another_inserted_person.id).unwrap();
// Delete the community
let community_num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
assert_eq!(1, community_num_deleted);
// Should be none found // Should be none found
let after_delete = PersonAggregates::read(&conn, inserted_person.id); let after_delete = PersonAggregates::read(&conn, inserted_person.id);
assert!(after_delete.is_err()); assert!(after_delete.is_err());

@ -62,7 +62,6 @@ mod tests {
let new_community = CommunityForm { let new_community = CommunityForm {
name: "TIL_community_agg".into(), name: "TIL_community_agg".into(),
creator_id: inserted_person.id,
title: "nada".to_owned(), title: "nada".to_owned(),
..CommunityForm::default() ..CommunityForm::default()
}; };
@ -149,6 +148,10 @@ mod tests {
let person_num_deleted = Person::delete(&conn, inserted_person.id).unwrap(); let person_num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
assert_eq!(1, person_num_deleted); assert_eq!(1, person_num_deleted);
// Delete the community
let community_num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
assert_eq!(1, community_num_deleted);
// Should be none found, since the creator was deleted // Should be none found, since the creator was deleted
let after_delete = PostAggregates::read(&conn, inserted_post.id); let after_delete = PostAggregates::read(&conn, inserted_post.id);
assert!(after_delete.is_err()); assert!(after_delete.is_err());

@ -49,6 +49,7 @@ mod tests {
let site_form = SiteForm { let site_form = SiteForm {
name: "test_site".into(), name: "test_site".into(),
sidebar: None,
description: None, description: None,
icon: None, icon: None,
banner: None, banner: None,
@ -63,7 +64,6 @@ mod tests {
let new_community = CommunityForm { let new_community = CommunityForm {
name: "TIL_site_agg".into(), name: "TIL_site_agg".into(),
creator_id: inserted_person.id,
title: "nada".to_owned(), title: "nada".to_owned(),
..CommunityForm::default() ..CommunityForm::default()
}; };
@ -118,6 +118,10 @@ mod tests {
let person_num_deleted = Person::delete(&conn, inserted_person.id).unwrap(); let person_num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
assert_eq!(1, person_num_deleted); assert_eq!(1, person_num_deleted);
// Delete the community
let community_num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
assert_eq!(1, community_num_deleted);
let after_delete = SiteAggregates::read(&conn); let after_delete = SiteAggregates::read(&conn);
assert!(after_delete.is_err()); assert!(after_delete.is_err());
} }

@ -254,7 +254,6 @@ mod tests {
let new_community = CommunityForm { let new_community = CommunityForm {
name: "test community".to_string(), name: "test community".to_string(),
title: "nada".to_owned(), title: "nada".to_owned(),
creator_id: inserted_person.id,
..CommunityForm::default() ..CommunityForm::default()
}; };

@ -26,7 +26,6 @@ mod safe_type {
name, name,
title, title,
description, description,
creator_id,
removed, removed,
published, published,
updated, updated,
@ -46,7 +45,6 @@ mod safe_type {
name, name,
title, title,
description, description,
creator_id,
removed, removed,
published, published,
updated, updated,
@ -122,16 +120,6 @@ pub trait Community_ {
community_id: CommunityId, community_id: CommunityId,
new_removed: bool, new_removed: bool,
) -> Result<Community, Error>; ) -> Result<Community, Error>;
fn update_removed_for_creator(
conn: &PgConnection,
for_creator_id: PersonId,
new_removed: bool,
) -> Result<Vec<Community>, Error>;
fn update_creator(
conn: &PgConnection,
community_id: CommunityId,
new_creator_id: PersonId,
) -> Result<Community, Error>;
fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error>; fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error>;
fn read_from_followers_url( fn read_from_followers_url(
conn: &PgConnection, conn: &PgConnection,
@ -170,28 +158,6 @@ impl Community_ for Community {
.get_result::<Self>(conn) .get_result::<Self>(conn)
} }
fn update_removed_for_creator(
conn: &PgConnection,
for_creator_id: PersonId,
new_removed: bool,
) -> Result<Vec<Community>, Error> {
use lemmy_db_schema::schema::community::dsl::*;
diesel::update(community.filter(creator_id.eq(for_creator_id)))
.set((removed.eq(new_removed), updated.eq(naive_now())))
.get_results::<Self>(conn)
}
fn update_creator(
conn: &PgConnection,
community_id: CommunityId,
new_creator_id: PersonId,
) -> Result<Community, Error> {
use lemmy_db_schema::schema::community::dsl::*;
diesel::update(community.find(community_id))
.set((creator_id.eq(new_creator_id), updated.eq(naive_now())))
.get_result::<Self>(conn)
}
fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error> { fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error> {
use lemmy_db_schema::schema::community::dsl::*; use lemmy_db_schema::schema::community::dsl::*;
community.select(actor_id).distinct().load::<String>(conn) community.select(actor_id).distinct().load::<String>(conn)
@ -363,7 +329,6 @@ mod tests {
let new_community = CommunityForm { let new_community = CommunityForm {
name: "TIL".into(), name: "TIL".into(),
creator_id: inserted_person.id,
title: "nada".to_owned(), title: "nada".to_owned(),
..CommunityForm::default() ..CommunityForm::default()
}; };
@ -372,7 +337,6 @@ mod tests {
let expected_community = Community { let expected_community = Community {
id: inserted_community.id, id: inserted_community.id,
creator_id: inserted_person.id,
name: "TIL".into(), name: "TIL".into(),
title: "nada".to_owned(), title: "nada".to_owned(),
description: None, description: None,

@ -24,6 +24,7 @@ mod safe_settings_type {
show_avatars, show_avatars,
send_notifications_to_email, send_notifications_to_email,
validator_time, validator_time,
show_scores,
); );
impl ToSafeSettings for LocalUser { impl ToSafeSettings for LocalUser {
@ -43,6 +44,7 @@ mod safe_settings_type {
show_avatars, show_avatars,
send_notifications_to_email, send_notifications_to_email,
validator_time, validator_time,
show_scores,
) )
} }
} }

@ -224,7 +224,6 @@ mod tests {
let new_community = CommunityForm { let new_community = CommunityForm {
name: "mod_community".to_string(), name: "mod_community".to_string(),
title: "nada".to_owned(), title: "nada".to_owned(),
creator_id: inserted_person.id,
..CommunityForm::default() ..CommunityForm::default()
}; };

@ -15,7 +15,7 @@ mod safe_type {
type Columns = ( type Columns = (
id, id,
name, name,
preferred_username, display_name,
avatar, avatar,
banned, banned,
published, published,
@ -37,7 +37,7 @@ mod safe_type {
( (
id, id,
name, name,
preferred_username, display_name,
avatar, avatar,
banned, banned,
published, published,
@ -63,7 +63,7 @@ mod safe_type_alias_1 {
type Columns = ( type Columns = (
id, id,
name, name,
preferred_username, display_name,
avatar, avatar,
banned, banned,
published, published,
@ -85,7 +85,7 @@ mod safe_type_alias_1 {
( (
id, id,
name, name,
preferred_username, display_name,
avatar, avatar,
banned, banned,
published, published,
@ -111,7 +111,7 @@ mod safe_type_alias_2 {
type Columns = ( type Columns = (
id, id,
name, name,
preferred_username, display_name,
avatar, avatar,
banned, banned,
published, published,
@ -133,7 +133,7 @@ mod safe_type_alias_2 {
( (
id, id,
name, name,
preferred_username, display_name,
avatar, avatar,
banned, banned,
published, published,
@ -236,7 +236,7 @@ impl Person_ for Person {
diesel::update(person.find(person_id)) diesel::update(person.find(person_id))
.set(( .set((
preferred_username.eq::<Option<String>>(None), display_name.eq::<Option<String>>(None),
bio.eq::<Option<String>>(None), bio.eq::<Option<String>>(None),
matrix_user_id.eq::<Option<String>>(None), matrix_user_id.eq::<Option<String>>(None),
deleted.eq(true), deleted.eq(true),
@ -264,7 +264,7 @@ mod tests {
let expected_person = Person { let expected_person = Person {
id: inserted_person.id, id: inserted_person.id,
name: "holly".into(), name: "holly".into(),
preferred_username: None, display_name: None,
avatar: None, avatar: None,
banner: None, banner: None,
banned: false, banned: false,

@ -105,7 +105,6 @@ mod tests {
let new_community = CommunityForm { let new_community = CommunityForm {
name: "test community lake".to_string(), name: "test community lake".to_string(),
title: "nada".to_owned(), title: "nada".to_owned(),
creator_id: inserted_person.id,
..CommunityForm::default() ..CommunityForm::default()
}; };

@ -281,7 +281,6 @@ mod tests {
let new_community = CommunityForm { let new_community = CommunityForm {
name: "test community_3".to_string(), name: "test community_3".to_string(),
title: "nada".to_owned(), title: "nada".to_owned(),
creator_id: inserted_person.id,
..CommunityForm::default() ..CommunityForm::default()
}; };

@ -78,7 +78,6 @@ table! {
name -> Varchar, name -> Varchar,
title -> Varchar, title -> Varchar,
description -> Nullable<Text>, description -> Nullable<Text>,
creator_id -> Int4,
removed -> Bool, removed -> Bool,
published -> Timestamp, published -> Timestamp,
updated -> Nullable<Timestamp>, updated -> Nullable<Timestamp>,
@ -154,6 +153,7 @@ table! {
show_avatars -> Bool, show_avatars -> Bool,
send_notifications_to_email -> Bool, send_notifications_to_email -> Bool,
validator_time -> Timestamp, validator_time -> Timestamp,
show_scores -> Bool,
} }
} }
@ -270,7 +270,7 @@ table! {
person (id) { person (id) {
id -> Int4, id -> Int4,
name -> Varchar, name -> Varchar,
preferred_username -> Nullable<Varchar>, display_name -> Nullable<Varchar>,
avatar -> Nullable<Varchar>, avatar -> Nullable<Varchar>,
banned -> Bool, banned -> Bool,
published -> Timestamp, published -> Timestamp,
@ -421,7 +421,7 @@ table! {
site (id) { site (id) {
id -> Int4, id -> Int4,
name -> Varchar, name -> Varchar,
description -> Nullable<Text>, sidebar -> Nullable<Text>,
creator_id -> Int4, creator_id -> Int4,
published -> Timestamp, published -> Timestamp,
updated -> Nullable<Timestamp>, updated -> Nullable<Timestamp>,
@ -430,6 +430,7 @@ table! {
enable_nsfw -> Bool, enable_nsfw -> Bool,
icon -> Nullable<Varchar>, icon -> Nullable<Varchar>,
banner -> Nullable<Varchar>, banner -> Nullable<Varchar>,
description -> Nullable<Text>,
} }
} }
@ -470,7 +471,7 @@ table! {
person_alias_1 (id) { person_alias_1 (id) {
id -> Int4, id -> Int4,
name -> Varchar, name -> Varchar,
preferred_username -> Nullable<Varchar>, display_name -> Nullable<Varchar>,
avatar -> Nullable<Varchar>, avatar -> Nullable<Varchar>,
banned -> Bool, banned -> Bool,
published -> Timestamp, published -> Timestamp,
@ -494,7 +495,7 @@ table! {
person_alias_2 (id) { person_alias_2 (id) {
id -> Int4, id -> Int4,
name -> Varchar, name -> Varchar,
preferred_username -> Nullable<Varchar>, display_name -> Nullable<Varchar>,
avatar -> Nullable<Varchar>, avatar -> Nullable<Varchar>,
banned -> Bool, banned -> Bool,
published -> Timestamp, published -> Timestamp,
@ -532,7 +533,6 @@ joinable!(comment_like -> post (post_id));
joinable!(comment_report -> comment (comment_id)); joinable!(comment_report -> comment (comment_id));
joinable!(comment_saved -> comment (comment_id)); joinable!(comment_saved -> comment (comment_id));
joinable!(comment_saved -> person (person_id)); joinable!(comment_saved -> person (person_id));
joinable!(community -> person (creator_id));
joinable!(community_aggregates -> community (community_id)); joinable!(community_aggregates -> community (community_id));
joinable!(community_follower -> community (community_id)); joinable!(community_follower -> community (community_id));
joinable!(community_follower -> person (person_id)); joinable!(community_follower -> person (person_id));

@ -56,8 +56,8 @@ pub struct CommentAlias1 {
pub struct CommentForm { pub struct CommentForm {
pub creator_id: PersonId, pub creator_id: PersonId,
pub post_id: PostId, pub post_id: PostId,
pub parent_id: Option<CommentId>,
pub content: String, pub content: String,
pub parent_id: Option<CommentId>,
pub removed: Option<bool>, pub removed: Option<bool>,
pub read: Option<bool>, pub read: Option<bool>,
pub published: Option<chrono::NaiveDateTime>, pub published: Option<chrono::NaiveDateTime>,

@ -13,7 +13,6 @@ pub struct Community {
pub name: String, pub name: String,
pub title: String, pub title: String,
pub description: Option<String>, pub description: Option<String>,
pub creator_id: PersonId,
pub removed: bool, pub removed: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
@ -39,7 +38,6 @@ pub struct CommunitySafe {
pub name: String, pub name: String,
pub title: String, pub title: String,
pub description: Option<String>, pub description: Option<String>,
pub creator_id: PersonId,
pub removed: bool, pub removed: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
@ -57,7 +55,6 @@ pub struct CommunityForm {
pub name: String, pub name: String,
pub title: String, pub title: String,
pub description: Option<String>, pub description: Option<String>,
pub creator_id: PersonId,
pub removed: Option<bool>, pub removed: Option<bool>,
pub published: Option<chrono::NaiveDateTime>, pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,

@ -16,6 +16,7 @@ pub struct LocalUser {
pub show_avatars: bool, pub show_avatars: bool,
pub send_notifications_to_email: bool, pub send_notifications_to_email: bool,
pub validator_time: chrono::NaiveDateTime, pub validator_time: chrono::NaiveDateTime,
pub show_scores: bool,
} }
// TODO redo these, check table defaults // TODO redo these, check table defaults
@ -32,6 +33,7 @@ pub struct LocalUserForm {
pub lang: Option<String>, pub lang: Option<String>,
pub show_avatars: Option<bool>, pub show_avatars: Option<bool>,
pub send_notifications_to_email: Option<bool>, pub send_notifications_to_email: Option<bool>,
pub show_scores: Option<bool>,
} }
/// A local user view that removes password encrypted /// A local user view that removes password encrypted
@ -49,4 +51,5 @@ pub struct LocalUserSettings {
pub show_avatars: bool, pub show_avatars: bool,
pub send_notifications_to_email: bool, pub send_notifications_to_email: bool,
pub validator_time: chrono::NaiveDateTime, pub validator_time: chrono::NaiveDateTime,
pub show_scores: bool,
} }

@ -10,7 +10,7 @@ use serde::Serialize;
pub struct Person { pub struct Person {
pub id: PersonId, pub id: PersonId,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub display_name: Option<String>,
pub avatar: Option<DbUrl>, pub avatar: Option<DbUrl>,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
@ -35,7 +35,7 @@ pub struct Person {
pub struct PersonSafe { pub struct PersonSafe {
pub id: PersonId, pub id: PersonId,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub display_name: Option<String>,
pub avatar: Option<DbUrl>, pub avatar: Option<DbUrl>,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
@ -56,7 +56,7 @@ pub struct PersonSafe {
pub struct PersonAlias1 { pub struct PersonAlias1 {
pub id: PersonId, pub id: PersonId,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub display_name: Option<String>,
pub avatar: Option<DbUrl>, pub avatar: Option<DbUrl>,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
@ -80,7 +80,7 @@ pub struct PersonAlias1 {
pub struct PersonSafeAlias1 { pub struct PersonSafeAlias1 {
pub id: PersonId, pub id: PersonId,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub display_name: Option<String>,
pub avatar: Option<DbUrl>, pub avatar: Option<DbUrl>,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
@ -101,7 +101,7 @@ pub struct PersonSafeAlias1 {
pub struct PersonAlias2 { pub struct PersonAlias2 {
pub id: PersonId, pub id: PersonId,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub display_name: Option<String>,
pub avatar: Option<DbUrl>, pub avatar: Option<DbUrl>,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
@ -125,7 +125,7 @@ pub struct PersonAlias2 {
pub struct PersonSafeAlias2 { pub struct PersonSafeAlias2 {
pub id: PersonId, pub id: PersonId,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub display_name: Option<String>,
pub avatar: Option<DbUrl>, pub avatar: Option<DbUrl>,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
@ -145,7 +145,7 @@ pub struct PersonSafeAlias2 {
#[table_name = "person"] #[table_name = "person"]
pub struct PersonForm { pub struct PersonForm {
pub name: String, pub name: String,
pub preferred_username: Option<Option<String>>, pub display_name: Option<Option<String>>,
pub avatar: Option<Option<DbUrl>>, pub avatar: Option<Option<DbUrl>>,
pub banned: Option<bool>, pub banned: Option<bool>,
pub published: Option<chrono::NaiveDateTime>, pub published: Option<chrono::NaiveDateTime>,

@ -35,16 +35,16 @@ pub struct Post {
#[table_name = "post"] #[table_name = "post"]
pub struct PostForm { pub struct PostForm {
pub name: String, pub name: String,
pub url: Option<DbUrl>,
pub body: Option<String>,
pub creator_id: PersonId, pub creator_id: PersonId,
pub community_id: CommunityId, pub community_id: CommunityId,
pub nsfw: bool,
pub url: Option<DbUrl>,
pub body: Option<String>,
pub removed: Option<bool>, pub removed: Option<bool>,
pub locked: Option<bool>, pub locked: Option<bool>,
pub published: Option<chrono::NaiveDateTime>, pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub deleted: Option<bool>, pub deleted: Option<bool>,
pub nsfw: bool,
pub stickied: Option<bool>, pub stickied: Option<bool>,
pub embed_title: Option<String>, pub embed_title: Option<String>,
pub embed_description: Option<String>, pub embed_description: Option<String>,

@ -6,7 +6,7 @@ use serde::Serialize;
pub struct Site { pub struct Site {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub description: Option<String>, pub sidebar: Option<String>,
pub creator_id: PersonId, pub creator_id: PersonId,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
@ -15,13 +15,14 @@ pub struct Site {
pub enable_nsfw: bool, pub enable_nsfw: bool,
pub icon: Option<DbUrl>, pub icon: Option<DbUrl>,
pub banner: Option<DbUrl>, pub banner: Option<DbUrl>,
pub description: Option<String>,
} }
#[derive(Insertable, AsChangeset)] #[derive(Insertable, AsChangeset)]
#[table_name = "site"] #[table_name = "site"]
pub struct SiteForm { pub struct SiteForm {
pub name: String, pub name: String,
pub description: Option<String>, pub sidebar: Option<Option<String>>,
pub creator_id: PersonId, pub creator_id: PersonId,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub enable_downvotes: bool, pub enable_downvotes: bool,
@ -30,4 +31,5 @@ pub struct SiteForm {
// when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column. // when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column.
pub icon: Option<Option<DbUrl>>, pub icon: Option<Option<DbUrl>>,
pub banner: Option<Option<DbUrl>>, pub banner: Option<Option<DbUrl>>,
pub description: Option<Option<String>>,
} }

@ -462,7 +462,6 @@ mod tests {
let new_community = CommunityForm { let new_community = CommunityForm {
name: "test community 5".to_string(), name: "test community 5".to_string(),
title: "nada".to_owned(), title: "nada".to_owned(),
creator_id: inserted_person.id,
..CommunityForm::default() ..CommunityForm::default()
}; };
@ -519,7 +518,7 @@ mod tests {
creator: PersonSafe { creator: PersonSafe {
id: inserted_person.id, id: inserted_person.id,
name: "timmy".into(), name: "timmy".into(),
preferred_username: None, display_name: None,
published: inserted_person.published, published: inserted_person.published,
avatar: None, avatar: None,
actor_id: inserted_person.actor_id.to_owned(), actor_id: inserted_person.actor_id.to_owned(),
@ -567,7 +566,6 @@ mod tests {
local: true, local: true,
title: "nada".to_owned(), title: "nada".to_owned(),
description: None, description: None,
creator_id: inserted_person.id,
updated: None, updated: None,
banner: None, banner: None,
published: inserted_community.published, published: inserted_community.published,

@ -263,7 +263,7 @@ impl<'a> PostQueryBuilder<'a> {
community_person_ban::table.on( community_person_ban::table.on(
post::community_id post::community_id
.eq(community_person_ban::community_id) .eq(community_person_ban::community_id)
.and(community_person_ban::person_id.eq(community::creator_id)), .and(community_person_ban::person_id.eq(post::creator_id)),
), ),
) )
.inner_join(post_aggregates::table) .inner_join(post_aggregates::table)
@ -462,7 +462,6 @@ mod tests {
let new_community = CommunityForm { let new_community = CommunityForm {
name: community_name.to_owned(), name: community_name.to_owned(),
title: "nada".to_owned(), title: "nada".to_owned(),
creator_id: inserted_person.id,
..CommunityForm::default() ..CommunityForm::default()
}; };
@ -541,7 +540,7 @@ mod tests {
creator: PersonSafe { creator: PersonSafe {
id: inserted_person.id, id: inserted_person.id,
name: person_name, name: person_name,
preferred_username: None, display_name: None,
published: inserted_person.published, published: inserted_person.published,
avatar: None, avatar: None,
actor_id: inserted_person.actor_id.to_owned(), actor_id: inserted_person.actor_id.to_owned(),
@ -568,7 +567,6 @@ mod tests {
local: true, local: true,
title: "nada".to_owned(), title: "nada".to_owned(),
description: None, description: None,
creator_id: inserted_person.id,
updated: None, updated: None,
banner: None, banner: None,
published: inserted_community.published, published: inserted_community.published,

@ -12,11 +12,8 @@ use lemmy_db_queries::{
ViewToVec, ViewToVec,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
schema::{community, community_aggregates, community_follower, person}, schema::{community, community_aggregates, community_follower},
source::{ source::community::{Community, CommunityFollower, CommunitySafe},
community::{Community, CommunityFollower, CommunitySafe},
person::{Person, PersonSafe},
},
CommunityId, CommunityId,
PersonId, PersonId,
}; };
@ -25,14 +22,12 @@ use serde::Serialize;
#[derive(Debug, Serialize, Clone)] #[derive(Debug, Serialize, Clone)]
pub struct CommunityView { pub struct CommunityView {
pub community: CommunitySafe, pub community: CommunitySafe,
pub creator: PersonSafe,
pub subscribed: bool, pub subscribed: bool,
pub counts: CommunityAggregates, pub counts: CommunityAggregates,
} }
type CommunityViewTuple = ( type CommunityViewTuple = (
CommunitySafe, CommunitySafe,
PersonSafe,
CommunityAggregates, CommunityAggregates,
Option<CommunityFollower>, Option<CommunityFollower>,
); );
@ -46,9 +41,8 @@ impl CommunityView {
// The left join below will return None in this case // The left join below will return None in this case
let person_id_join = my_person_id.unwrap_or(PersonId(-1)); let person_id_join = my_person_id.unwrap_or(PersonId(-1));
let (community, creator, counts, follower) = community::table let (community, counts, follower) = community::table
.find(community_id) .find(community_id)
.inner_join(person::table)
.inner_join(community_aggregates::table) .inner_join(community_aggregates::table)
.left_join( .left_join(
community_follower::table.on( community_follower::table.on(
@ -59,7 +53,6 @@ impl CommunityView {
) )
.select(( .select((
Community::safe_columns_tuple(), Community::safe_columns_tuple(),
Person::safe_columns_tuple(),
community_aggregates::all_columns, community_aggregates::all_columns,
community_follower::all_columns.nullable(), community_follower::all_columns.nullable(),
)) ))
@ -67,7 +60,6 @@ impl CommunityView {
Ok(CommunityView { Ok(CommunityView {
community, community,
creator,
subscribed: follower.is_some(), subscribed: follower.is_some(),
counts, counts,
}) })
@ -165,7 +157,6 @@ impl<'a> CommunityQueryBuilder<'a> {
let person_id_join = self.my_person_id.unwrap_or(PersonId(-1)); let person_id_join = self.my_person_id.unwrap_or(PersonId(-1));
let mut query = community::table let mut query = community::table
.inner_join(person::table)
.inner_join(community_aggregates::table) .inner_join(community_aggregates::table)
.left_join( .left_join(
community_follower::table.on( community_follower::table.on(
@ -176,7 +167,6 @@ impl<'a> CommunityQueryBuilder<'a> {
) )
.select(( .select((
Community::safe_columns_tuple(), Community::safe_columns_tuple(),
Person::safe_columns_tuple(),
community_aggregates::all_columns, community_aggregates::all_columns,
community_follower::all_columns.nullable(), community_follower::all_columns.nullable(),
)) ))
@ -193,6 +183,7 @@ impl<'a> CommunityQueryBuilder<'a> {
match self.sort { match self.sort {
SortType::New => query = query.order_by(community::published.desc()), SortType::New => query = query.order_by(community::published.desc()),
SortType::TopAll => query = query.order_by(community_aggregates::subscribers.desc()), SortType::TopAll => query = query.order_by(community_aggregates::subscribers.desc()),
SortType::TopMonth => query = query.order_by(community_aggregates::users_active_month.desc()),
// Covers all other sorts, including hot // Covers all other sorts, including hot
_ => { _ => {
query = query query = query
@ -236,9 +227,8 @@ impl ViewToVec for CommunityView {
.iter() .iter()
.map(|a| Self { .map(|a| Self {
community: a.0.to_owned(), community: a.0.to_owned(),
creator: a.1.to_owned(), counts: a.1.to_owned(),
counts: a.2.to_owned(), subscribed: a.2.is_some(),
subscribed: a.3.is_some(),
}) })
.collect::<Vec<Self>>() .collect::<Vec<Self>>()
} }

@ -24,12 +24,12 @@ impl Default for Settings {
impl Default for DatabaseConfig { impl Default for DatabaseConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
user: "lemmy".into(), user: Some("lemmy".to_string()),
password: "password".into(), password: "password".into(),
host: "localhost".into(), host: "localhost".into(),
port: 5432, port: Some(5432),
database: "lemmy".into(), database: Some("lemmy".to_string()),
pool_size: 5, pool_size: Some(5),
} }
} }
} }

@ -16,7 +16,7 @@ use deser_hjson::from_str;
use merge::Merge; use merge::Merge;
use std::{env, fs, io::Error, net::IpAddr, sync::RwLock}; use std::{env, fs, io::Error, net::IpAddr, sync::RwLock};
pub(crate) mod defaults; pub mod defaults;
pub mod structs; pub mod structs;
static CONFIG_FILE: &str = "config/config.hjson"; static CONFIG_FILE: &str = "config/config.hjson";
@ -60,7 +60,11 @@ impl Settings {
let conf = self.database(); let conf = self.database();
format!( format!(
"postgres://{}:{}@{}:{}/{}", "postgres://{}:{}@{}:{}/{}",
conf.user, conf.password, conf.host, conf.port, conf.database, conf.user(),
conf.password,
conf.host,
conf.port(),
conf.database(),
) )
} }

@ -27,12 +27,40 @@ pub struct CaptchaConfig {
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
pub struct DatabaseConfig { pub struct DatabaseConfig {
pub user: String, pub(super) user: Option<String>,
pub password: String, pub password: String,
pub host: String, pub host: String,
pub port: i32, pub(super) port: Option<i32>,
pub database: String, pub(super) database: Option<String>,
pub pool_size: u32, pub(super) pool_size: Option<u32>,
}
impl DatabaseConfig {
pub fn user(&self) -> String {
self
.user
.to_owned()
.unwrap_or_else(|| DatabaseConfig::default().user.expect("missing user"))
}
pub fn port(&self) -> i32 {
self
.port
.unwrap_or_else(|| DatabaseConfig::default().port.expect("missing port"))
}
pub fn database(&self) -> String {
self.database.to_owned().unwrap_or_else(|| {
DatabaseConfig::default()
.database
.expect("missing database")
})
}
pub fn pool_size(&self) -> u32 {
self.pool_size.unwrap_or_else(|| {
DatabaseConfig::default()
.pool_size
.expect("missing pool_size")
})
}
} }
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]

@ -1,7 +1,8 @@
use crate::utils::{ use crate::utils::{
is_valid_community_name, is_valid_community_name,
is_valid_display_name,
is_valid_matrix_id,
is_valid_post_title, is_valid_post_title,
is_valid_preferred_username,
is_valid_username, is_valid_username,
remove_slurs, remove_slurs,
scrape_text_for_mentions, scrape_text_for_mentions,
@ -29,9 +30,15 @@ fn test_valid_register_username() {
} }
#[test] #[test]
fn test_valid_preferred_username() { fn test_valid_display_name() {
assert!(is_valid_preferred_username("hello @there")); assert!(is_valid_display_name("hello @there"));
assert!(!is_valid_preferred_username("@hello there")); assert!(!is_valid_display_name("@hello there"));
// Make sure zero-space with an @ doesn't work
assert!(!is_valid_display_name(&format!(
"{}@my name is",
'\u{200b}'
)));
} }
#[test] #[test]
@ -50,6 +57,14 @@ fn test_valid_post_title() {
assert!(!is_valid_post_title("\n \n \n \n ")); // tabs/spaces/newlines assert!(!is_valid_post_title("\n \n \n \n ")); // tabs/spaces/newlines
} }
#[test]
fn test_valid_matrix_id() {
assert!(is_valid_matrix_id("@dess:matrix.org"));
assert!(!is_valid_matrix_id("dess:matrix.org"));
assert!(!is_valid_matrix_id(" @dess:matrix.org"));
assert!(!is_valid_matrix_id("@dess:matrix.org t"));
}
#[test] #[test]
fn test_slur_filter() { fn test_slur_filter() {
let test = let test =

@ -15,6 +15,7 @@ lazy_static! {
static ref VALID_USERNAME_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9_]{3,20}$").expect("compile regex"); static ref VALID_USERNAME_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9_]{3,20}$").expect("compile regex");
static ref VALID_COMMUNITY_NAME_REGEX: Regex = Regex::new(r"^[a-z0-9_]{3,20}$").expect("compile regex"); static ref VALID_COMMUNITY_NAME_REGEX: Regex = Regex::new(r"^[a-z0-9_]{3,20}$").expect("compile regex");
static ref VALID_POST_TITLE_REGEX: Regex = Regex::new(r".*\S.*").expect("compile regex"); static ref VALID_POST_TITLE_REGEX: Regex = Regex::new(r".*\S.*").expect("compile regex");
static ref VALID_MATRIX_ID_REGEX: Regex = Regex::new(r"^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$").expect("compile regex");
} }
pub fn naive_from_unix(time: i64) -> NaiveDateTime { pub fn naive_from_unix(time: i64) -> NaiveDateTime {
@ -108,10 +109,15 @@ pub fn is_valid_username(name: &str) -> bool {
} }
// Can't do a regex here, reverse lookarounds not supported // Can't do a regex here, reverse lookarounds not supported
pub fn is_valid_preferred_username(preferred_username: &str) -> bool { pub fn is_valid_display_name(name: &str) -> bool {
!preferred_username.starts_with('@') !name.starts_with('@')
&& preferred_username.chars().count() >= 3 && !name.starts_with('\u{200b}')
&& preferred_username.chars().count() <= 20 && name.chars().count() >= 3
&& name.chars().count() <= 20
}
pub fn is_valid_matrix_id(matrix_id: &str) -> bool {
VALID_MATRIX_ID_REGEX.is_match(matrix_id)
} }
pub fn is_valid_community_name(name: &str) -> bool { pub fn is_valid_community_name(name: &str) -> bool {

@ -1 +1 @@
pub const VERSION: &str = "0.10.2"; pub const VERSION: &str = "0.10.3";

@ -123,6 +123,7 @@ pub enum UserOperation {
PostJoin, PostJoin,
CommunityJoin, CommunityJoin,
ModJoin, ModJoin,
ChangePassword,
} }
#[derive(EnumString, ToString, Debug, Clone)] #[derive(EnumString, ToString, Debug, Clone)]

@ -49,9 +49,6 @@ FROM alpine:3.12 as lemmy
# Install libpq for postgres # Install libpq for postgres
RUN apk add libpq RUN apk add libpq
# Install Espeak for captchas
RUN apk add espeak
RUN addgroup -g 1000 lemmy RUN addgroup -g 1000 lemmy
RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy

@ -9,4 +9,4 @@ mkdir -p volumes/pictrs
sudo chown -R 991:991 volumes/pictrs sudo chown -R 991:991 volumes/pictrs
sudo docker build ../../ --file ../dev/volume_mount.dockerfile -t lemmy-dev:latest sudo docker build ../../ --file ../dev/volume_mount.dockerfile -t lemmy-dev:latest
sudo docker-compose pull --ignore-pull-failures || true sudo docker-compose pull --ignore-pull-failures || true
sudo docker-compose up -d sudo docker-compose up

@ -19,9 +19,9 @@ RUN --mount=type=cache,target=/app/target \
FROM ubuntu:20.10 FROM ubuntu:20.10
# Install libpq for postgres and espeak # Install libpq for postgres
RUN apt-get update -y RUN apt-get update -y
RUN apt-get install -y libpq-dev espeak RUN apt-get install -y libpq-dev
# Copy resources # Copy resources
COPY --from=rust /app/lemmy_server /app/lemmy COPY --from=rust /app/lemmy_server /app/lemmy

@ -22,9 +22,9 @@ RUN cp ./target/release/lemmy_server /app/lemmy_server
# The Debian runner # The Debian runner
FROM debian:buster-slim as lemmy FROM debian:buster-slim as lemmy
# Install libpq for postgres and espeak for captchas # Install libpq for postgres
RUN apt-get update \ RUN apt-get update \
&& apt-get -y install --no-install-recommends espeak postgresql-client libc6 libssl1.1 \ && apt-get -y install --no-install-recommends postgresql-client libc6 libssl1.1 \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN addgroup --gid 1000 lemmy RUN addgroup --gid 1000 lemmy

@ -12,7 +12,7 @@ services:
restart: always restart: always
lemmy: lemmy:
image: dessalines/lemmy:0.10.2 image: dessalines/lemmy:0.10.3
ports: ports:
- "127.0.0.1:8536:8536" - "127.0.0.1:8536:8536"
restart: always restart: always
@ -26,7 +26,7 @@ services:
- iframely - iframely
lemmy-ui: lemmy-ui:
image: dessalines/lemmy-ui:0.10.2 image: dessalines/lemmy-ui:0.10.3
ports: ports:
- "127.0.0.1:1235:1234" - "127.0.0.1:1235:1234"
restart: always restart: always

@ -0,0 +1 @@
alter table local_user drop column show_scores;

@ -0,0 +1 @@
alter table local_user add column show_scores boolean default true not null;

@ -0,0 +1,2 @@
alter table site drop column description;
alter table site rename column sidebar to description;

@ -0,0 +1,5 @@
-- Renaming description to sidebar
alter table site rename column description to sidebar;
-- Adding a short description column
alter table site add column description varchar(150);

@ -0,0 +1,6 @@
alter table person rename display_name to preferred_username;
-- Regenerate the person_alias views
drop view person_alias_1, person_alias_2;
create view person_alias_1 as select * from person;
create view person_alias_2 as select * from person;

@ -0,0 +1,6 @@
alter table person rename preferred_username to display_name;
-- Regenerate the person_alias views
drop view person_alias_1, person_alias_2;
create view person_alias_1 as select * from person;
create view person_alias_2 as select * from person;

@ -0,0 +1 @@
drop index idx_community_aggregates_users_active_month;

@ -0,0 +1,2 @@
create index idx_community_aggregates_users_active_month on community_aggregates (users_active_month desc);

@ -0,0 +1,24 @@
-- Add the column back
alter table community add column creator_id int references person on update cascade on delete cascade;
-- Recreate the index
create index idx_community_creator on community (creator_id);
-- Add the data, selecting the highest mod
update community
set creator_id = sub.person_id
from (
select
cm.community_id,
cm.person_id
from
community_moderator cm
limit 1
) as sub
where id = sub.community_id;
-- Set to not null
alter table community alter column creator_id set not null;

@ -0,0 +1,2 @@
-- Drop the column
alter table community drop column creator_id;

@ -182,6 +182,10 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
"/save_user_settings", "/save_user_settings",
web::put().to(route_post::<SaveUserSettings>), web::put().to(route_post::<SaveUserSettings>),
) )
.route(
"/change_password",
web::put().to(route_post::<ChangePassword>),
)
.route("/report_count", web::get().to(route_get::<GetReportCount>)), .route("/report_count", web::get().to(route_get::<GetReportCount>)),
) )
// Admin Actions // Admin Actions

@ -89,7 +89,6 @@ fn community_updates_2020_04_02(conn: &PgConnection) -> Result<(), LemmyError> {
name: ccommunity.name.to_owned(), name: ccommunity.name.to_owned(),
title: ccommunity.title.to_owned(), title: ccommunity.title.to_owned(),
description: ccommunity.description.to_owned(), description: ccommunity.description.to_owned(),
creator_id: ccommunity.creator_id,
removed: None, removed: None,
deleted: None, deleted: None,
nsfw: None, nsfw: None,

@ -38,7 +38,7 @@ async fn main() -> Result<(), LemmyError> {
}; };
let manager = ConnectionManager::<PgConnection>::new(&db_url); let manager = ConnectionManager::<PgConnection>::new(&db_url);
let pool = Pool::builder() let pool = Pool::builder()
.max_size(settings.database().pool_size) .max_size(settings.database().pool_size())
.build(manager) .build(manager)
.unwrap_or_else(|_| panic!("Error connecting to {}", db_url)); .unwrap_or_else(|_| panic!("Error connecting to {}", db_url));

Loading…
Cancel
Save