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,
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 =
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
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;
}
}
+ 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
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,
}
}
+ 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()),
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(),
setupTippy,
hostname,
pictrsDeleteToast,
+ validTitle,
} from '../utils';
import autosize from 'autosize';
import Tribute from 'tributejs/src/Tribute.js';
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">
// // 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);
+}
"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"
}