mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-10-01 01:36:12 -04:00
Forbid users to use empty titles for posts (#930)
- Add a regex that checks if string contains anything but whitespace - Check for whitespace-only titles on post creation and edit - Trim whitespace from titles before saving - Add frontend validation to title
This commit is contained in:
parent
7a9a973c89
commit
8d24659892
@ -158,12 +158,17 @@ pub fn is_valid_community_name(name: &str) -> bool {
|
||||
VALID_COMMUNITY_NAME_REGEX.is_match(name)
|
||||
}
|
||||
|
||||
pub fn is_valid_post_title(title: &str) -> bool {
|
||||
VALID_POST_TITLE_REGEX.is_match(title)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
is_email_regex,
|
||||
is_valid_community_name,
|
||||
is_valid_username,
|
||||
is_valid_post_title,
|
||||
remove_slurs,
|
||||
scrape_text_for_mentions,
|
||||
slur_check,
|
||||
@ -204,6 +209,15 @@ mod tests {
|
||||
assert!(!is_valid_community_name(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_valid_post_title() {
|
||||
assert!(is_valid_post_title("Post Title"));
|
||||
assert!(is_valid_post_title(" POST TITLE 😃😃😃😃😃"));
|
||||
assert!(!is_valid_post_title("\n \n \n \n ")); // tabs/spaces/newlines
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_slur_filter() {
|
||||
let test =
|
||||
@ -249,6 +263,7 @@ lazy_static! {
|
||||
static ref MENTIONS_REGEX: Regex = Regex::new(r"@(?P<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._:-]+)").unwrap();
|
||||
static ref VALID_USERNAME_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9_]{3,20}$").unwrap();
|
||||
static ref VALID_COMMUNITY_NAME_REGEX: Regex = Regex::new(r"^[a-z0-9_]{3,20}$").unwrap();
|
||||
static ref VALID_POST_TITLE_REGEX: Regex = Regex::new(r".*\S.*").unwrap();
|
||||
pub static ref WEBFINGER_COMMUNITY_REGEX: Regex = Regex::new(&format!(
|
||||
"^group:([a-z0-9_]{{3, 20}})@{}$",
|
||||
Settings::get().hostname
|
||||
|
@ -28,7 +28,7 @@ use lemmy_db::{
|
||||
Saveable,
|
||||
SortType,
|
||||
};
|
||||
use lemmy_utils::{make_apub_endpoint, slur_check, slurs_vec_to_str, EndpointType};
|
||||
use lemmy_utils::{is_valid_post_title, make_apub_endpoint, slur_check, slurs_vec_to_str, EndpointType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
@ -135,6 +135,10 @@ impl Perform for Oper<CreatePost> {
|
||||
}
|
||||
}
|
||||
|
||||
if !is_valid_post_title(&data.name) {
|
||||
return Err(APIError::err("invalid_post_title").into());
|
||||
}
|
||||
|
||||
let user_id = claims.id;
|
||||
|
||||
// Check for a community ban
|
||||
@ -156,7 +160,7 @@ impl Perform for Oper<CreatePost> {
|
||||
fetch_iframely_and_pictrs_data(&self.client, data.url.to_owned()).await;
|
||||
|
||||
let post_form = PostForm {
|
||||
name: data.name.to_owned(),
|
||||
name: data.name.trim().to_owned(),
|
||||
url: data.url.to_owned(),
|
||||
body: data.body.to_owned(),
|
||||
community_id: data.community_id,
|
||||
@ -516,6 +520,10 @@ impl Perform for Oper<EditPost> {
|
||||
}
|
||||
}
|
||||
|
||||
if !is_valid_post_title(&data.name) {
|
||||
return Err(APIError::err("invalid_post_title").into());
|
||||
}
|
||||
|
||||
let claims = match Claims::decode(&data.auth) {
|
||||
Ok(claims) => claims.claims,
|
||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
||||
@ -565,7 +573,7 @@ impl Perform for Oper<EditPost> {
|
||||
let read_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
|
||||
|
||||
let post_form = PostForm {
|
||||
name: data.name.to_owned(),
|
||||
name: data.name.trim().to_owned(),
|
||||
url: data.url.to_owned(),
|
||||
body: data.body.to_owned(),
|
||||
creator_id: data.creator_id.to_owned(),
|
||||
|
10
ui/src/components/post-form.tsx
vendored
10
ui/src/components/post-form.tsx
vendored
@ -35,6 +35,7 @@ import {
|
||||
setupTippy,
|
||||
hostname,
|
||||
pictrsDeleteToast,
|
||||
validTitle,
|
||||
} from '../utils';
|
||||
import autosize from 'autosize';
|
||||
import Tribute from 'tributejs/src/Tribute.js';
|
||||
@ -271,12 +272,19 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||
value={this.state.postForm.name}
|
||||
id="post-title"
|
||||
onInput={linkEvent(this, this.handlePostNameChange)}
|
||||
class="form-control"
|
||||
class={`form-control ${
|
||||
!validTitle(this.state.postForm.name) && 'is-invalid'
|
||||
}`}
|
||||
required
|
||||
rows={2}
|
||||
minLength={3}
|
||||
maxLength={MAX_POST_TITLE_LENGTH}
|
||||
/>
|
||||
{!validTitle(this.state.postForm.name) && (
|
||||
<div class="invalid-feedback">
|
||||
{i18n.t('invalid_post_title')}
|
||||
</div>
|
||||
)}
|
||||
{this.state.suggestedPosts.length > 0 && (
|
||||
<>
|
||||
<div class="my-1 text-muted small font-weight-bold">
|
||||
|
9
ui/src/utils.ts
vendored
9
ui/src/utils.ts
vendored
@ -986,3 +986,12 @@ function canUseWebP() {
|
||||
// // very old browser like IE 8, canvas not supported
|
||||
// return false;
|
||||
}
|
||||
|
||||
export function validTitle(title?: string): boolean {
|
||||
// Initial title is null, minimum length is taken care of by textarea's minLength={3}
|
||||
if (title === null || title.length < 3) return true;
|
||||
|
||||
const regex = new RegExp(/.*\S.*/, 'g');
|
||||
|
||||
return regex.test(title);
|
||||
}
|
||||
|
3
ui/translations/en.json
vendored
3
ui/translations/en.json
vendored
@ -268,5 +268,6 @@
|
||||
"block_leaving": "Are you sure you want to leave?",
|
||||
"what_is": "What is",
|
||||
"cake_day_title": "Cake day:",
|
||||
"cake_day_info": "It's {{ creator_name }}'s cake day today!"
|
||||
"cake_day_info": "It's {{ creator_name }}'s cake day today!",
|
||||
"invalid_post_title": "Invalid post title"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user