mirror of
https://git.joinplu.me/Plume/Plume.git
synced 2024-11-25 13:01:08 +00:00
Merge pull request 'allow timeline manipulation from plm' (#1113) from timeline-cli into main
Reviewed-on: https://git.joinplu.me/Plume/Plume/pulls/1113 Reviewed-by: KitaitiMakoto <kitaitimakoto@noreply@joinplu.me>
This commit is contained in:
commit
97cbe7f446
16 changed files with 661 additions and 96 deletions
262
plume-cli/src/list.rs
Normal file
262
plume-cli/src/list.rs
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||||
|
|
||||||
|
use plume_models::{blogs::Blog, instance::Instance, lists::*, users::User, Connection};
|
||||||
|
|
||||||
|
pub fn command<'a, 'b>() -> App<'a, 'b> {
|
||||||
|
SubCommand::with_name("lists")
|
||||||
|
.about("Manage lists")
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("new")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("name")
|
||||||
|
.short("n")
|
||||||
|
.long("name")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The name of this list"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("type")
|
||||||
|
.short("t")
|
||||||
|
.long("type")
|
||||||
|
.takes_value(true)
|
||||||
|
.help(
|
||||||
|
r#"The type of this list (one of "user", "blog", "word" or "prefix")"#,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("user")
|
||||||
|
.short("u")
|
||||||
|
.long("user")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Username of whom this list is for. Empty for an instance list"),
|
||||||
|
)
|
||||||
|
.about("Create a new list"),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("delete")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("name")
|
||||||
|
.short("n")
|
||||||
|
.long("name")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The name of the list to delete"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("user")
|
||||||
|
.short("u")
|
||||||
|
.long("user")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Username of whom this list was for. Empty for instance list"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("yes")
|
||||||
|
.short("y")
|
||||||
|
.long("yes")
|
||||||
|
.help("Confirm the deletion"),
|
||||||
|
)
|
||||||
|
.about("Delete a list"),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("add")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("name")
|
||||||
|
.short("n")
|
||||||
|
.long("name")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The name of the list to add an element to"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("user")
|
||||||
|
.short("u")
|
||||||
|
.long("user")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Username of whom this list is for. Empty for instance list"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("value")
|
||||||
|
.short("v")
|
||||||
|
.long("value")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The value to add"),
|
||||||
|
)
|
||||||
|
.about("Add element to a list"),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("rm")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("name")
|
||||||
|
.short("n")
|
||||||
|
.long("name")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The name of the list to remove an element from"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("user")
|
||||||
|
.short("u")
|
||||||
|
.long("user")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Username of whom this list is for. Empty for instance list"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("value")
|
||||||
|
.short("v")
|
||||||
|
.long("value")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The value to remove"),
|
||||||
|
)
|
||||||
|
.about("Remove element from list"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run<'a>(args: &ArgMatches<'a>, conn: &Connection) {
|
||||||
|
let conn = conn;
|
||||||
|
match args.subcommand() {
|
||||||
|
("new", Some(x)) => new(x, conn),
|
||||||
|
("delete", Some(x)) => delete(x, conn),
|
||||||
|
("add", Some(x)) => add(x, conn),
|
||||||
|
("rm", Some(x)) => rm(x, conn),
|
||||||
|
("", None) => command().print_help().unwrap(),
|
||||||
|
_ => println!("Unknown subcommand"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_list_identifier(args: &ArgMatches<'_>) -> (String, Option<String>) {
|
||||||
|
let name = args
|
||||||
|
.value_of("name")
|
||||||
|
.map(String::from)
|
||||||
|
.expect("No name provided for the list");
|
||||||
|
let user = args.value_of("user").map(String::from);
|
||||||
|
(name, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_list_type(args: &ArgMatches<'_>) -> ListType {
|
||||||
|
let typ = args
|
||||||
|
.value_of("type")
|
||||||
|
.map(String::from)
|
||||||
|
.expect("No name type for the list");
|
||||||
|
match typ.as_str() {
|
||||||
|
"user" => ListType::User,
|
||||||
|
"blog" => ListType::Blog,
|
||||||
|
"word" => ListType::Word,
|
||||||
|
"prefix" => ListType::Prefix,
|
||||||
|
_ => panic!("Invalid list type: {}", typ),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_value(args: &ArgMatches<'_>) -> String {
|
||||||
|
args.value_of("value")
|
||||||
|
.map(String::from)
|
||||||
|
.expect("No query provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_user(username: &str, conn: &Connection) -> User {
|
||||||
|
let instance = Instance::get_local_uncached(conn).expect("Failed to load local instance");
|
||||||
|
|
||||||
|
User::find_by_name(conn, username, instance.id).expect("User not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(args: &ArgMatches<'_>, conn: &Connection) {
|
||||||
|
let (name, user) = get_list_identifier(args);
|
||||||
|
let typ = get_list_type(args);
|
||||||
|
|
||||||
|
let user = user.map(|user| resolve_user(&user, conn));
|
||||||
|
|
||||||
|
List::new(conn, &name, user.as_ref(), typ).expect("failed to create list");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete(args: &ArgMatches<'_>, conn: &Connection) {
|
||||||
|
let (name, user) = get_list_identifier(args);
|
||||||
|
|
||||||
|
if !args.is_present("yes") {
|
||||||
|
panic!("Warning, this operation is destructive. Add --yes to confirm you want to do it.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let user = user.map(|user| resolve_user(&user, conn));
|
||||||
|
|
||||||
|
let list =
|
||||||
|
List::find_for_user_by_name(conn, user.map(|u| u.id), &name).expect("list not found");
|
||||||
|
|
||||||
|
list.delete(conn).expect("Failed to update list");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(args: &ArgMatches<'_>, conn: &Connection) {
|
||||||
|
let (name, user) = get_list_identifier(args);
|
||||||
|
let value = get_value(args);
|
||||||
|
|
||||||
|
let user = user.map(|user| resolve_user(&user, conn));
|
||||||
|
|
||||||
|
let list =
|
||||||
|
List::find_for_user_by_name(conn, user.map(|u| u.id), &name).expect("list not found");
|
||||||
|
|
||||||
|
match list.kind() {
|
||||||
|
ListType::Blog => {
|
||||||
|
let blog_id = Blog::find_by_fqn(conn, &value).expect("unknown blog").id;
|
||||||
|
if !list.contains_blog(conn, blog_id).unwrap() {
|
||||||
|
list.add_blogs(conn, &[blog_id]).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListType::User => {
|
||||||
|
let user_id = User::find_by_fqn(conn, &value).expect("unknown user").id;
|
||||||
|
if !list.contains_user(conn, user_id).unwrap() {
|
||||||
|
list.add_users(conn, &[user_id]).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListType::Word => {
|
||||||
|
if !list.contains_word(conn, &value).unwrap() {
|
||||||
|
list.add_words(conn, &[&value]).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListType::Prefix => {
|
||||||
|
if !list.contains_prefix(conn, &value).unwrap() {
|
||||||
|
list.add_prefixes(conn, &[&value]).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rm(args: &ArgMatches<'_>, conn: &Connection) {
|
||||||
|
let (name, user) = get_list_identifier(args);
|
||||||
|
let value = get_value(args);
|
||||||
|
|
||||||
|
let user = user.map(|user| resolve_user(&user, conn));
|
||||||
|
|
||||||
|
let list =
|
||||||
|
List::find_for_user_by_name(conn, user.map(|u| u.id), &name).expect("list not found");
|
||||||
|
|
||||||
|
match list.kind() {
|
||||||
|
ListType::Blog => {
|
||||||
|
let blog_id = Blog::find_by_fqn(conn, &value).expect("unknown blog").id;
|
||||||
|
let mut blogs = list.list_blogs(conn).unwrap();
|
||||||
|
if let Some(index) = blogs.iter().position(|b| b.id == blog_id) {
|
||||||
|
blogs.swap_remove(index);
|
||||||
|
let blogs = blogs.iter().map(|b| b.id).collect::<Vec<_>>();
|
||||||
|
list.set_blogs(conn, &blogs).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListType::User => {
|
||||||
|
let user_id = User::find_by_fqn(conn, &value).expect("unknown user").id;
|
||||||
|
let mut users = list.list_users(conn).unwrap();
|
||||||
|
if let Some(index) = users.iter().position(|u| u.id == user_id) {
|
||||||
|
users.swap_remove(index);
|
||||||
|
let users = users.iter().map(|u| u.id).collect::<Vec<_>>();
|
||||||
|
list.set_users(conn, &users).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListType::Word => {
|
||||||
|
let mut words = list.list_words(conn).unwrap();
|
||||||
|
if let Some(index) = words.iter().position(|w| *w == value) {
|
||||||
|
words.swap_remove(index);
|
||||||
|
let words = words.iter().map(String::as_str).collect::<Vec<_>>();
|
||||||
|
list.set_words(conn, &words).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListType::Prefix => {
|
||||||
|
let mut prefixes = list.list_prefixes(conn).unwrap();
|
||||||
|
if let Some(index) = prefixes.iter().position(|p| *p == value) {
|
||||||
|
prefixes.swap_remove(index);
|
||||||
|
let prefixes = prefixes.iter().map(String::as_str).collect::<Vec<_>>();
|
||||||
|
list.set_prefixes(conn, &prefixes).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,8 +4,10 @@ use plume_models::{instance::Instance, Connection as Conn, CONFIG};
|
||||||
use std::io::{self, prelude::*};
|
use std::io::{self, prelude::*};
|
||||||
|
|
||||||
mod instance;
|
mod instance;
|
||||||
|
mod list;
|
||||||
mod migration;
|
mod migration;
|
||||||
mod search;
|
mod search;
|
||||||
|
mod timeline;
|
||||||
mod users;
|
mod users;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -16,6 +18,8 @@ fn main() {
|
||||||
.subcommand(instance::command())
|
.subcommand(instance::command())
|
||||||
.subcommand(migration::command())
|
.subcommand(migration::command())
|
||||||
.subcommand(search::command())
|
.subcommand(search::command())
|
||||||
|
.subcommand(timeline::command())
|
||||||
|
.subcommand(list::command())
|
||||||
.subcommand(users::command());
|
.subcommand(users::command());
|
||||||
let matches = app.clone().get_matches();
|
let matches = app.clone().get_matches();
|
||||||
|
|
||||||
|
@ -37,6 +41,10 @@ fn main() {
|
||||||
("search", Some(args)) => {
|
("search", Some(args)) => {
|
||||||
search::run(args, &conn.expect("Couldn't connect to the database."))
|
search::run(args, &conn.expect("Couldn't connect to the database."))
|
||||||
}
|
}
|
||||||
|
("timeline", Some(args)) => {
|
||||||
|
timeline::run(args, &conn.expect("Couldn't connect to the database."))
|
||||||
|
}
|
||||||
|
("lists", Some(args)) => list::run(args, &conn.expect("Couldn't connect to the database.")),
|
||||||
("users", Some(args)) => {
|
("users", Some(args)) => {
|
||||||
users::run(args, &conn.expect("Couldn't connect to the database."))
|
users::run(args, &conn.expect("Couldn't connect to the database."))
|
||||||
}
|
}
|
||||||
|
|
257
plume-cli/src/timeline.rs
Normal file
257
plume-cli/src/timeline.rs
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||||
|
|
||||||
|
use plume_models::{instance::Instance, posts::Post, timeline::*, users::*, Connection};
|
||||||
|
|
||||||
|
pub fn command<'a, 'b>() -> App<'a, 'b> {
|
||||||
|
SubCommand::with_name("timeline")
|
||||||
|
.about("Manage public timeline")
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("new")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("name")
|
||||||
|
.short("n")
|
||||||
|
.long("name")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The name of this timeline"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("query")
|
||||||
|
.short("q")
|
||||||
|
.long("query")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The query posts in this timelines have to match"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("user")
|
||||||
|
.short("u")
|
||||||
|
.long("user")
|
||||||
|
.takes_value(true)
|
||||||
|
.help(
|
||||||
|
"Username of whom this timeline is for. Empty for an instance timeline",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("preload-count")
|
||||||
|
.short("p")
|
||||||
|
.long("preload-count")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Number of posts to try to preload in this timeline at its creation"),
|
||||||
|
)
|
||||||
|
.about("Create a new timeline"),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("delete")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("name")
|
||||||
|
.short("n")
|
||||||
|
.long("name")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The name of the timeline to delete"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("user")
|
||||||
|
.short("u")
|
||||||
|
.long("user")
|
||||||
|
.takes_value(true)
|
||||||
|
.help(
|
||||||
|
"Username of whom this timeline was for. Empty for instance timeline",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("yes")
|
||||||
|
.short("y")
|
||||||
|
.long("yes")
|
||||||
|
.help("Confirm the deletion"),
|
||||||
|
)
|
||||||
|
.about("Delete a timeline"),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("edit")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("name")
|
||||||
|
.short("n")
|
||||||
|
.long("name")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The name of the timeline to edit"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("user")
|
||||||
|
.short("u")
|
||||||
|
.long("user")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Username of whom this timeline is for. Empty for instance timeline"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("query")
|
||||||
|
.short("q")
|
||||||
|
.long("query")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The query posts in this timelines have to match"),
|
||||||
|
)
|
||||||
|
.about("Edit the query of a timeline"),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("repopulate")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("name")
|
||||||
|
.short("n")
|
||||||
|
.long("name")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The name of the timeline to repopulate"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("user")
|
||||||
|
.short("u")
|
||||||
|
.long("user")
|
||||||
|
.takes_value(true)
|
||||||
|
.help(
|
||||||
|
"Username of whom this timeline was for. Empty for instance timeline",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("preload-count")
|
||||||
|
.short("p")
|
||||||
|
.long("preload-count")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Number of posts to try to preload in this timeline at its creation"),
|
||||||
|
)
|
||||||
|
.about("Repopulate a timeline. Run this after modifying a list the timeline depends on."),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run<'a>(args: &ArgMatches<'a>, conn: &Connection) {
|
||||||
|
let conn = conn;
|
||||||
|
match args.subcommand() {
|
||||||
|
("new", Some(x)) => new(x, conn),
|
||||||
|
("edit", Some(x)) => edit(x, conn),
|
||||||
|
("delete", Some(x)) => delete(x, conn),
|
||||||
|
("repopulate", Some(x)) => repopulate(x, conn),
|
||||||
|
("", None) => command().print_help().unwrap(),
|
||||||
|
_ => println!("Unknown subcommand"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_timeline_identifier(args: &ArgMatches<'_>) -> (String, Option<String>) {
|
||||||
|
let name = args
|
||||||
|
.value_of("name")
|
||||||
|
.map(String::from)
|
||||||
|
.expect("No name provided for the timeline");
|
||||||
|
let user = args.value_of("user").map(String::from);
|
||||||
|
(name, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_query(args: &ArgMatches<'_>) -> String {
|
||||||
|
let query = args
|
||||||
|
.value_of("query")
|
||||||
|
.map(String::from)
|
||||||
|
.expect("No query provided");
|
||||||
|
|
||||||
|
match TimelineQuery::parse(&query) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(QueryError::SyntaxError(start, end, message)) => panic!(
|
||||||
|
"Query parsing error between {} and {}: {}",
|
||||||
|
start, end, message
|
||||||
|
),
|
||||||
|
Err(QueryError::UnexpectedEndOfQuery) => {
|
||||||
|
panic!("Query parsing error: unexpected end of query")
|
||||||
|
}
|
||||||
|
Err(QueryError::RuntimeError(message)) => panic!("Query parsing error: {}", message),
|
||||||
|
}
|
||||||
|
|
||||||
|
query
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_preload_count(args: &ArgMatches<'_>) -> usize {
|
||||||
|
args.value_of("preload-count")
|
||||||
|
.map(|arg| arg.parse().expect("invalid preload-count"))
|
||||||
|
.unwrap_or(plume_models::ITEMS_PER_PAGE as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_user(username: &str, conn: &Connection) -> User {
|
||||||
|
let instance = Instance::get_local_uncached(conn).expect("Failed to load local instance");
|
||||||
|
|
||||||
|
User::find_by_name(conn, username, instance.id).expect("User not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn preload(timeline: Timeline, count: usize, conn: &Connection) {
|
||||||
|
timeline.remove_all_posts(conn).unwrap();
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut posts = Vec::with_capacity(count as usize);
|
||||||
|
for post in Post::list_filtered(conn, None, None, None)
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.rev()
|
||||||
|
{
|
||||||
|
if timeline.matches(conn, &post, Kind::Original).unwrap() {
|
||||||
|
posts.push(post);
|
||||||
|
if posts.len() >= count {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for post in posts.iter().rev() {
|
||||||
|
timeline.add_post(conn, post).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(args: &ArgMatches<'_>, conn: &Connection) {
|
||||||
|
let (name, user) = get_timeline_identifier(args);
|
||||||
|
let query = get_query(args);
|
||||||
|
let preload_count = get_preload_count(args);
|
||||||
|
|
||||||
|
let user = user.map(|user| resolve_user(&user, conn));
|
||||||
|
|
||||||
|
let timeline = if let Some(user) = user {
|
||||||
|
Timeline::new_for_user(conn, user.id, name, query)
|
||||||
|
} else {
|
||||||
|
Timeline::new_for_instance(conn, name, query)
|
||||||
|
}
|
||||||
|
.expect("Failed to create new timeline");
|
||||||
|
|
||||||
|
preload(timeline, preload_count, conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edit(args: &ArgMatches<'_>, conn: &Connection) {
|
||||||
|
let (name, user) = get_timeline_identifier(args);
|
||||||
|
let query = get_query(args);
|
||||||
|
|
||||||
|
let user = user.map(|user| resolve_user(&user, conn));
|
||||||
|
|
||||||
|
let mut timeline = Timeline::find_for_user_by_name(conn, user.map(|u| u.id), &name)
|
||||||
|
.expect("timeline not found");
|
||||||
|
|
||||||
|
timeline.query = query;
|
||||||
|
|
||||||
|
timeline.update(conn).expect("Failed to update timeline");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete(args: &ArgMatches<'_>, conn: &Connection) {
|
||||||
|
let (name, user) = get_timeline_identifier(args);
|
||||||
|
|
||||||
|
if !args.is_present("yes") {
|
||||||
|
panic!("Warning, this operation is destructive. Add --yes to confirm you want to do it.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let user = user.map(|user| resolve_user(&user, conn));
|
||||||
|
|
||||||
|
let timeline = Timeline::find_for_user_by_name(conn, user.map(|u| u.id), &name)
|
||||||
|
.expect("timeline not found");
|
||||||
|
|
||||||
|
timeline.delete(conn).expect("Failed to update timeline");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn repopulate(args: &ArgMatches<'_>, conn: &Connection) {
|
||||||
|
let (name, user) = get_timeline_identifier(args);
|
||||||
|
let preload_count = get_preload_count(args);
|
||||||
|
|
||||||
|
let user = user.map(|user| resolve_user(&user, conn));
|
||||||
|
|
||||||
|
let timeline = Timeline::find_for_user_by_name(conn, user.map(|u| u.id), &name)
|
||||||
|
.expect("timeline not found");
|
||||||
|
preload(timeline, preload_count, conn);
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
db_conn::DbConn, instance::*, medias::Media, posts::Post, safe_string::SafeString,
|
instance::*, medias::Media, posts::Post, safe_string::SafeString, schema::blogs, users::User,
|
||||||
schema::blogs, users::User, Connection, Error, PlumeRocket, Result, CONFIG, ITEMS_PER_PAGE,
|
Connection, Error, PlumeRocket, Result, CONFIG, ITEMS_PER_PAGE,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
actor::{ApActor, ApActorExt, AsApActor, Group},
|
actor::{ApActor, ApActorExt, AsApActor, Group},
|
||||||
|
@ -142,10 +142,10 @@ impl Blog {
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_fqn(conn: &DbConn, fqn: &str) -> Result<Blog> {
|
pub fn find_by_fqn(conn: &Connection, fqn: &str) -> Result<Blog> {
|
||||||
let from_db = blogs::table
|
let from_db = blogs::table
|
||||||
.filter(blogs::fqn.eq(fqn))
|
.filter(blogs::fqn.eq(fqn))
|
||||||
.first(&**conn)
|
.first(conn)
|
||||||
.optional()?;
|
.optional()?;
|
||||||
if let Some(from_db) = from_db {
|
if let Some(from_db) = from_db {
|
||||||
Ok(from_db)
|
Ok(from_db)
|
||||||
|
@ -154,7 +154,7 @@ impl Blog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_from_webfinger(conn: &DbConn, acct: &str) -> Result<Blog> {
|
fn fetch_from_webfinger(conn: &Connection, acct: &str) -> Result<Blog> {
|
||||||
resolve_with_prefix(Prefix::Group, acct.to_owned(), true)?
|
resolve_with_prefix(Prefix::Group, acct.to_owned(), true)?
|
||||||
.links
|
.links
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -372,15 +372,15 @@ impl IntoId for Blog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for Blog {
|
impl FromId<Connection> for Blog {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = CustomGroup;
|
type Object = CustomGroup;
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
fn from_db(conn: &Connection, id: &str) -> Result<Self> {
|
||||||
Self::find_by_ap_url(conn, id)
|
Self::find_by_ap_url(conn, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, acct: CustomGroup) -> Result<Self> {
|
fn from_activity(conn: &Connection, acct: CustomGroup) -> Result<Self> {
|
||||||
let (name, outbox_url, inbox_url) = {
|
let (name, outbox_url, inbox_url) = {
|
||||||
let actor = acct.ap_actor_ref();
|
let actor = acct.ap_actor_ref();
|
||||||
let name = actor
|
let name = actor
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
comment_seers::{CommentSeers, NewCommentSeers},
|
comment_seers::{CommentSeers, NewCommentSeers},
|
||||||
db_conn::DbConn,
|
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
medias::Media,
|
medias::Media,
|
||||||
mentions::Mention,
|
mentions::Mention,
|
||||||
|
@ -111,7 +110,7 @@ impl Comment {
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_activity(&self, conn: &DbConn) -> Result<Note> {
|
pub fn to_activity(&self, conn: &Connection) -> Result<Note> {
|
||||||
let author = User::get(conn, self.author_id)?;
|
let author = User::get(conn, self.author_id)?;
|
||||||
let (html, mentions, _hashtags) = utils::md_to_html(
|
let (html, mentions, _hashtags) = utils::md_to_html(
|
||||||
self.content.get().as_ref(),
|
self.content.get().as_ref(),
|
||||||
|
@ -149,7 +148,7 @@ impl Comment {
|
||||||
Ok(note)
|
Ok(note)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_activity(&self, conn: &DbConn) -> Result<Create> {
|
pub fn create_activity(&self, conn: &Connection) -> Result<Create> {
|
||||||
let author = User::get(conn, self.author_id)?;
|
let author = User::get(conn, self.author_id)?;
|
||||||
|
|
||||||
let note = self.to_activity(conn)?;
|
let note = self.to_activity(conn)?;
|
||||||
|
@ -217,15 +216,15 @@ impl Comment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for Comment {
|
impl FromId<Connection> for Comment {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = Note;
|
type Object = Note;
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
fn from_db(conn: &Connection, id: &str) -> Result<Self> {
|
||||||
Self::find_by_ap_url(conn, id)
|
Self::find_by_ap_url(conn, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, note: Note) -> Result<Self> {
|
fn from_activity(conn: &Connection, note: Note) -> Result<Self> {
|
||||||
let comm = {
|
let comm = {
|
||||||
let previous_url = note
|
let previous_url = note
|
||||||
.in_reply_to()
|
.in_reply_to()
|
||||||
|
@ -354,21 +353,21 @@ impl FromId<DbConn> for Comment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Create, &DbConn> for Comment {
|
impl AsObject<User, Create, &Connection> for Comment {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn activity(self, _conn: &DbConn, _actor: User, _id: &str) -> Result<Self> {
|
fn activity(self, _conn: &Connection, _actor: User, _id: &str) -> Result<Self> {
|
||||||
// The actual creation takes place in the FromId impl
|
// The actual creation takes place in the FromId impl
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Delete, &DbConn> for Comment {
|
impl AsObject<User, Delete, &Connection> for Comment {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
fn activity(self, conn: &Connection, actor: User, _id: &str) -> Result<()> {
|
||||||
if self.author_id != actor.id {
|
if self.author_id != actor.id {
|
||||||
return Err(Error::Unauthorized);
|
return Err(Error::Unauthorized);
|
||||||
}
|
}
|
||||||
|
@ -387,8 +386,8 @@ impl AsObject<User, Delete, &DbConn> for Comment {
|
||||||
diesel::update(comments::table)
|
diesel::update(comments::table)
|
||||||
.filter(comments::in_response_to_id.eq(self.id))
|
.filter(comments::in_response_to_id.eq(self.id))
|
||||||
.set(comments::in_response_to_id.eq(self.in_response_to_id))
|
.set(comments::in_response_to_id.eq(self.in_response_to_id))
|
||||||
.execute(&**conn)?;
|
.execute(conn)?;
|
||||||
diesel::delete(&self).execute(&**conn)?;
|
diesel::delete(&self).execute(conn)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ap_url, db_conn::DbConn, instance::Instance, notifications::*, schema::follows, users::User,
|
ap_url, instance::Instance, notifications::*, schema::follows, users::User, Connection, Error,
|
||||||
Connection, Error, Result, CONFIG,
|
Result, CONFIG,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{Accept, ActorAndObjectRef, Follow as FollowAct, Undo},
|
activity::{Accept, ActorAndObjectRef, Follow as FollowAct, Undo},
|
||||||
|
@ -150,11 +150,11 @@ impl Follow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, FollowAct, &DbConn> for User {
|
impl AsObject<User, FollowAct, &Connection> for User {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Output = Follow;
|
type Output = Follow;
|
||||||
|
|
||||||
fn activity(self, conn: &DbConn, actor: User, id: &str) -> Result<Follow> {
|
fn activity(self, conn: &Connection, actor: User, id: &str) -> Result<Follow> {
|
||||||
// Mastodon (at least) requires the full Follow object when accepting it,
|
// Mastodon (at least) requires the full Follow object when accepting it,
|
||||||
// so we rebuilt it here
|
// so we rebuilt it here
|
||||||
let follow = FollowAct::new(actor.ap_url.parse::<IriString>()?, id.parse::<IriString>()?);
|
let follow = FollowAct::new(actor.ap_url.parse::<IriString>()?, id.parse::<IriString>()?);
|
||||||
|
@ -162,15 +162,15 @@ impl AsObject<User, FollowAct, &DbConn> for User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for Follow {
|
impl FromId<Connection> for Follow {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = FollowAct;
|
type Object = FollowAct;
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
fn from_db(conn: &Connection, id: &str) -> Result<Self> {
|
||||||
Follow::find_by_ap_url(conn, id)
|
Follow::find_by_ap_url(conn, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, follow: FollowAct) -> Result<Self> {
|
fn from_activity(conn: &Connection, follow: FollowAct) -> Result<Self> {
|
||||||
let actor = User::from_id(
|
let actor = User::from_id(
|
||||||
conn,
|
conn,
|
||||||
follow
|
follow
|
||||||
|
@ -202,18 +202,18 @@ impl FromId<DbConn> for Follow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Undo, &DbConn> for Follow {
|
impl AsObject<User, Undo, &Connection> for Follow {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
fn activity(self, conn: &Connection, actor: User, _id: &str) -> Result<()> {
|
||||||
let conn = conn;
|
let conn = conn;
|
||||||
if self.follower_id == actor.id {
|
if self.follower_id == actor.id {
|
||||||
diesel::delete(&self).execute(&**conn)?;
|
diesel::delete(&self).execute(conn)?;
|
||||||
|
|
||||||
// delete associated notification if any
|
// delete associated notification if any
|
||||||
if let Ok(notif) = Notification::find(conn, notification_kind::FOLLOW, self.id) {
|
if let Ok(notif) = Notification::find(conn, notification_kind::FOLLOW, self.id) {
|
||||||
diesel::delete(¬if).execute(&**conn)?;
|
diesel::delete(¬if).execute(conn)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -47,7 +47,7 @@ impl_into_inbox_result! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inbox(conn: &DbConn, act: serde_json::Value) -> Result<InboxResult, Error> {
|
pub fn inbox(conn: &DbConn, act: serde_json::Value) -> Result<InboxResult, Error> {
|
||||||
Inbox::handle(conn, act)
|
Inbox::handle(&**conn, act)
|
||||||
.with::<User, Announce, Post>(CONFIG.proxy())
|
.with::<User, Announce, Post>(CONFIG.proxy())
|
||||||
.with::<User, Create, Comment>(CONFIG.proxy())
|
.with::<User, Create, Comment>(CONFIG.proxy())
|
||||||
.with::<User, Create, Post>(CONFIG.proxy())
|
.with::<User, Create, Post>(CONFIG.proxy())
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
db_conn::DbConn, instance::Instance, notifications::*, posts::Post, schema::likes, timeline::*,
|
instance::Instance, notifications::*, posts::Post, schema::likes, timeline::*, users::User,
|
||||||
users::User, Connection, Error, Result, CONFIG,
|
Connection, Error, Result, CONFIG,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{ActorAndObjectRef, Like as LikeAct, Undo},
|
activity::{ActorAndObjectRef, Like as LikeAct, Undo},
|
||||||
|
@ -85,11 +85,11 @@ impl Like {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, LikeAct, &DbConn> for Post {
|
impl AsObject<User, LikeAct, &Connection> for Post {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Output = Like;
|
type Output = Like;
|
||||||
|
|
||||||
fn activity(self, conn: &DbConn, actor: User, id: &str) -> Result<Like> {
|
fn activity(self, conn: &Connection, actor: User, id: &str) -> Result<Like> {
|
||||||
let res = Like::insert(
|
let res = Like::insert(
|
||||||
conn,
|
conn,
|
||||||
NewLike {
|
NewLike {
|
||||||
|
@ -105,15 +105,15 @@ impl AsObject<User, LikeAct, &DbConn> for Post {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for Like {
|
impl FromId<Connection> for Like {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = LikeAct;
|
type Object = LikeAct;
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
fn from_db(conn: &Connection, id: &str) -> Result<Self> {
|
||||||
Like::find_by_ap_url(conn, id)
|
Like::find_by_ap_url(conn, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, act: LikeAct) -> Result<Self> {
|
fn from_activity(conn: &Connection, act: LikeAct) -> Result<Self> {
|
||||||
let res = Like::insert(
|
let res = Like::insert(
|
||||||
conn,
|
conn,
|
||||||
NewLike {
|
NewLike {
|
||||||
|
@ -154,17 +154,17 @@ impl FromId<DbConn> for Like {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Undo, &DbConn> for Like {
|
impl AsObject<User, Undo, &Connection> for Like {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
fn activity(self, conn: &Connection, actor: User, _id: &str) -> Result<()> {
|
||||||
if actor.id == self.user_id {
|
if actor.id == self.user_id {
|
||||||
diesel::delete(&self).execute(&**conn)?;
|
diesel::delete(&self).execute(conn)?;
|
||||||
|
|
||||||
// delete associated notification if any
|
// delete associated notification if any
|
||||||
if let Ok(notif) = Notification::find(conn, notification_kind::LIKE, self.id) {
|
if let Ok(notif) = Notification::find(conn, notification_kind::LIKE, self.id) {
|
||||||
diesel::delete(¬if).execute(&**conn)?;
|
diesel::delete(¬if).execute(conn)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -297,6 +297,28 @@ impl List {
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn delete(&self, conn: &Connection) -> Result<()> {
|
||||||
|
if let Some(user_id) = self.user_id {
|
||||||
|
diesel::delete(
|
||||||
|
lists::table
|
||||||
|
.filter(lists::user_id.eq(user_id))
|
||||||
|
.filter(lists::name.eq(&self.name)),
|
||||||
|
)
|
||||||
|
.execute(conn)
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(Error::from)
|
||||||
|
} else {
|
||||||
|
diesel::delete(
|
||||||
|
lists::table
|
||||||
|
.filter(lists::user_id.is_null())
|
||||||
|
.filter(lists::name.eq(&self.name)),
|
||||||
|
)
|
||||||
|
.execute(conn)
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(Error::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func! {set: set_users, User, add_users}
|
func! {set: set_users, User, add_users}
|
||||||
func! {set: set_blogs, Blog, add_blogs}
|
func! {set: set_blogs, Blog, add_blogs}
|
||||||
func! {set: set_words, Word, add_words}
|
func! {set: set_words, Word, add_words}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ap_url, db_conn::DbConn, instance::Instance, safe_string::SafeString, schema::medias,
|
ap_url, instance::Instance, safe_string::SafeString, schema::medias, users::User, Connection,
|
||||||
users::User, Connection, Error, Result, CONFIG,
|
Error, Result, CONFIG,
|
||||||
};
|
};
|
||||||
use activitystreams::{object::Image, prelude::*};
|
use activitystreams::{object::Image, prelude::*};
|
||||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||||
|
@ -206,7 +206,7 @@ impl Media {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: merge with save_remote?
|
// TODO: merge with save_remote?
|
||||||
pub fn from_activity(conn: &DbConn, image: &Image) -> Result<Media> {
|
pub fn from_activity(conn: &Connection, image: &Image) -> Result<Media> {
|
||||||
let remote_url = image
|
let remote_url = image
|
||||||
.url()
|
.url()
|
||||||
.and_then(|url| url.to_as_uri())
|
.and_then(|url| url.to_as_uri())
|
||||||
|
@ -258,7 +258,7 @@ impl Media {
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
if updated {
|
if updated {
|
||||||
diesel::update(&media).set(&media).execute(&**conn)?;
|
diesel::update(&media).set(&media).execute(conn)?;
|
||||||
}
|
}
|
||||||
Ok(media)
|
Ok(media)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
comments::Comment, db_conn::DbConn, notifications::*, posts::Post, schema::mentions,
|
comments::Comment, notifications::*, posts::Post, schema::mentions, users::User, Connection,
|
||||||
users::User, Connection, Error, Result,
|
Error, Result,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
base::BaseExt,
|
base::BaseExt,
|
||||||
|
@ -60,7 +60,7 @@ impl Mention {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_activity(conn: &DbConn, ment: &str) -> Result<link::Mention> {
|
pub fn build_activity(conn: &Connection, ment: &str) -> Result<link::Mention> {
|
||||||
let user = User::find_by_fqn(conn, ment)?;
|
let user = User::find_by_fqn(conn, ment)?;
|
||||||
let mut mention = link::Mention::new();
|
let mut mention = link::Mention::new();
|
||||||
mention.set_href(user.ap_url.parse::<IriString>()?);
|
mention.set_href(user.ap_url.parse::<IriString>()?);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ap_url, blogs::Blog, db_conn::DbConn, instance::Instance, medias::Media, mentions::Mention,
|
ap_url, blogs::Blog, instance::Instance, medias::Media, mentions::Mention, post_authors::*,
|
||||||
post_authors::*, safe_string::SafeString, schema::posts, tags::*, timeline::*, users::User,
|
safe_string::SafeString, schema::posts, tags::*, timeline::*, users::User, Connection, Error,
|
||||||
Connection, Error, PostEvent::*, Result, CONFIG, POST_CHAN,
|
PostEvent::*, Result, CONFIG, POST_CHAN,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{Create, Delete, Update},
|
activity::{Create, Delete, Update},
|
||||||
|
@ -615,15 +615,15 @@ impl Post {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for Post {
|
impl FromId<Connection> for Post {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = LicensedArticle;
|
type Object = LicensedArticle;
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
fn from_db(conn: &Connection, id: &str) -> Result<Self> {
|
||||||
Self::find_by_ap_url(conn, id)
|
Self::find_by_ap_url(conn, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, article: LicensedArticle) -> Result<Self> {
|
fn from_activity(conn: &Connection, article: LicensedArticle) -> Result<Self> {
|
||||||
let license = article.ext_one.license.unwrap_or_default();
|
let license = article.ext_one.license.unwrap_or_default();
|
||||||
let article = article.inner;
|
let article = article.inner;
|
||||||
|
|
||||||
|
@ -821,21 +821,21 @@ impl FromId<DbConn> for Post {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Create, &DbConn> for Post {
|
impl AsObject<User, Create, &Connection> for Post {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn activity(self, _conn: &DbConn, _actor: User, _id: &str) -> Result<Self::Output> {
|
fn activity(self, _conn: &Connection, _actor: User, _id: &str) -> Result<Self::Output> {
|
||||||
// TODO: check that _actor is actually one of the author?
|
// TODO: check that _actor is actually one of the author?
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Delete, &DbConn> for Post {
|
impl AsObject<User, Delete, &Connection> for Post {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<Self::Output> {
|
fn activity(self, conn: &Connection, actor: User, _id: &str) -> Result<Self::Output> {
|
||||||
let can_delete = self
|
let can_delete = self
|
||||||
.get_authors(conn)?
|
.get_authors(conn)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -859,16 +859,16 @@ pub struct PostUpdate {
|
||||||
pub tags: Option<serde_json::Value>,
|
pub tags: Option<serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for PostUpdate {
|
impl FromId<Connection> for PostUpdate {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = LicensedArticle;
|
type Object = LicensedArticle;
|
||||||
|
|
||||||
fn from_db(_: &DbConn, _: &str) -> Result<Self> {
|
fn from_db(_: &Connection, _: &str) -> Result<Self> {
|
||||||
// Always fail because we always want to deserialize the AP object
|
// Always fail because we always want to deserialize the AP object
|
||||||
Err(Error::NotFound)
|
Err(Error::NotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, updated: Self::Object) -> Result<Self> {
|
fn from_activity(conn: &Connection, updated: Self::Object) -> Result<Self> {
|
||||||
let mut post_update = PostUpdate {
|
let mut post_update = PostUpdate {
|
||||||
ap_url: updated
|
ap_url: updated
|
||||||
.ap_object_ref()
|
.ap_object_ref()
|
||||||
|
@ -923,11 +923,11 @@ impl FromId<DbConn> for PostUpdate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Update, &DbConn> for PostUpdate {
|
impl AsObject<User, Update, &Connection> for PostUpdate {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
fn activity(self, conn: &Connection, actor: User, _id: &str) -> Result<()> {
|
||||||
let mut post =
|
let mut post =
|
||||||
Post::from_id(conn, &self.ap_url, None, CONFIG.proxy()).map_err(|(_, e)| e)?;
|
Post::from_id(conn, &self.ap_url, None, CONFIG.proxy()).map_err(|(_, e)| e)?;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
db_conn::DbConn, instance::Instance, notifications::*, posts::Post, schema::reshares,
|
instance::Instance, notifications::*, posts::Post, schema::reshares, timeline::*, users::User,
|
||||||
timeline::*, users::User, Connection, Error, Result, CONFIG,
|
Connection, Error, Result, CONFIG,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{ActorAndObjectRef, Announce, Undo},
|
activity::{ActorAndObjectRef, Announce, Undo},
|
||||||
|
@ -113,11 +113,11 @@ impl Reshare {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Announce, &DbConn> for Post {
|
impl AsObject<User, Announce, &Connection> for Post {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Output = Reshare;
|
type Output = Reshare;
|
||||||
|
|
||||||
fn activity(self, conn: &DbConn, actor: User, id: &str) -> Result<Reshare> {
|
fn activity(self, conn: &Connection, actor: User, id: &str) -> Result<Reshare> {
|
||||||
let conn = conn;
|
let conn = conn;
|
||||||
let reshare = Reshare::insert(
|
let reshare = Reshare::insert(
|
||||||
conn,
|
conn,
|
||||||
|
@ -134,15 +134,15 @@ impl AsObject<User, Announce, &DbConn> for Post {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromId<DbConn> for Reshare {
|
impl FromId<Connection> for Reshare {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = Announce;
|
type Object = Announce;
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
fn from_db(conn: &Connection, id: &str) -> Result<Self> {
|
||||||
Reshare::find_by_ap_url(conn, id)
|
Reshare::find_by_ap_url(conn, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, act: Announce) -> Result<Self> {
|
fn from_activity(conn: &Connection, act: Announce) -> Result<Self> {
|
||||||
let res = Reshare::insert(
|
let res = Reshare::insert(
|
||||||
conn,
|
conn,
|
||||||
NewReshare {
|
NewReshare {
|
||||||
|
@ -183,17 +183,17 @@ impl FromId<DbConn> for Reshare {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Undo, &DbConn> for Reshare {
|
impl AsObject<User, Undo, &Connection> for Reshare {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
fn activity(self, conn: &Connection, actor: User, _id: &str) -> Result<()> {
|
||||||
if actor.id == self.user_id {
|
if actor.id == self.user_id {
|
||||||
diesel::delete(&self).execute(&**conn)?;
|
diesel::delete(&self).execute(conn)?;
|
||||||
|
|
||||||
// delete associated notification if any
|
// delete associated notification if any
|
||||||
if let Ok(notif) = Notification::find(conn, notification_kind::RESHARE, self.id) {
|
if let Ok(notif) = Notification::find(conn, notification_kind::RESHARE, self.id) {
|
||||||
diesel::delete(¬if).execute(&**conn)?;
|
diesel::delete(¬if).execute(conn)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
db_conn::DbConn,
|
|
||||||
lists::List,
|
lists::List,
|
||||||
posts::Post,
|
posts::Post,
|
||||||
schema::{posts, timeline, timeline_definition},
|
schema::{posts, timeline, timeline_definition},
|
||||||
|
@ -12,7 +11,7 @@ use std::ops::Deref;
|
||||||
pub(crate) mod query;
|
pub(crate) mod query;
|
||||||
|
|
||||||
pub use self::query::Kind;
|
pub use self::query::Kind;
|
||||||
use self::query::{QueryError, TimelineQuery};
|
pub use self::query::{QueryError, TimelineQuery};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Queryable, Identifiable, AsChangeset)]
|
#[derive(Clone, Debug, PartialEq, Eq, Queryable, Identifiable, AsChangeset)]
|
||||||
#[table_name = "timeline_definition"]
|
#[table_name = "timeline_definition"]
|
||||||
|
@ -220,7 +219,7 @@ impl Timeline {
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_to_all_timelines(conn: &DbConn, post: &Post, kind: Kind<'_>) -> Result<()> {
|
pub fn add_to_all_timelines(conn: &Connection, post: &Post, kind: Kind<'_>) -> Result<()> {
|
||||||
let timelines = timeline_definition::table
|
let timelines = timeline_definition::table
|
||||||
.load::<Self>(conn.deref())
|
.load::<Self>(conn.deref())
|
||||||
.map_err(Error::from)?;
|
.map_err(Error::from)?;
|
||||||
|
@ -246,7 +245,26 @@ impl Timeline {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn matches(&self, conn: &DbConn, post: &Post, kind: Kind<'_>) -> Result<bool> {
|
pub fn remove_post(&self, conn: &Connection, post: &Post) -> Result<bool> {
|
||||||
|
if self.includes_post(conn, post)? {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
diesel::delete(
|
||||||
|
timeline::table
|
||||||
|
.filter(timeline::timeline_id.eq(self.id))
|
||||||
|
.filter(timeline::post_id.eq(post.id)),
|
||||||
|
)
|
||||||
|
.execute(conn)?;
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_all_posts(&self, conn: &Connection) -> Result<u64> {
|
||||||
|
let count = diesel::delete(timeline::table.filter(timeline::timeline_id.eq(self.id)))
|
||||||
|
.execute(conn)?;
|
||||||
|
Ok(count as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn matches(&self, conn: &Connection, post: &Post, kind: Kind<'_>) -> Result<bool> {
|
||||||
let query = TimelineQuery::parse(&self.query)?;
|
let query = TimelineQuery::parse(&self.query)?;
|
||||||
query.matches(conn, self, post, kind)
|
query.matches(conn, self, post, kind)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
blogs::Blog,
|
blogs::Blog,
|
||||||
db_conn::DbConn,
|
|
||||||
lists::{self, ListType},
|
lists::{self, ListType},
|
||||||
posts::Post,
|
posts::Post,
|
||||||
tags::Tag,
|
tags::Tag,
|
||||||
timeline::Timeline,
|
timeline::Timeline,
|
||||||
users::User,
|
users::User,
|
||||||
Result,
|
Connection, Result,
|
||||||
};
|
};
|
||||||
use plume_common::activity_pub::inbox::AsActor;
|
use plume_common::activity_pub::inbox::AsActor;
|
||||||
use whatlang::{self, Lang};
|
use whatlang::{self, Lang};
|
||||||
|
@ -155,7 +154,7 @@ enum TQ<'a> {
|
||||||
impl<'a> TQ<'a> {
|
impl<'a> TQ<'a> {
|
||||||
fn matches(
|
fn matches(
|
||||||
&self,
|
&self,
|
||||||
conn: &DbConn,
|
conn: &Connection,
|
||||||
timeline: &Timeline,
|
timeline: &Timeline,
|
||||||
post: &Post,
|
post: &Post,
|
||||||
kind: Kind<'_>,
|
kind: Kind<'_>,
|
||||||
|
@ -200,7 +199,7 @@ enum Arg<'a> {
|
||||||
impl<'a> Arg<'a> {
|
impl<'a> Arg<'a> {
|
||||||
pub fn matches(
|
pub fn matches(
|
||||||
&self,
|
&self,
|
||||||
conn: &DbConn,
|
conn: &Connection,
|
||||||
timeline: &Timeline,
|
timeline: &Timeline,
|
||||||
post: &Post,
|
post: &Post,
|
||||||
kind: Kind<'_>,
|
kind: Kind<'_>,
|
||||||
|
@ -225,7 +224,7 @@ enum WithList {
|
||||||
impl WithList {
|
impl WithList {
|
||||||
pub fn matches(
|
pub fn matches(
|
||||||
&self,
|
&self,
|
||||||
conn: &DbConn,
|
conn: &Connection,
|
||||||
timeline: &Timeline,
|
timeline: &Timeline,
|
||||||
post: &Post,
|
post: &Post,
|
||||||
list: &List<'_>,
|
list: &List<'_>,
|
||||||
|
@ -361,7 +360,7 @@ enum Bool {
|
||||||
impl Bool {
|
impl Bool {
|
||||||
pub fn matches(
|
pub fn matches(
|
||||||
&self,
|
&self,
|
||||||
conn: &DbConn,
|
conn: &Connection,
|
||||||
timeline: &Timeline,
|
timeline: &Timeline,
|
||||||
post: &Post,
|
post: &Post,
|
||||||
kind: Kind<'_>,
|
kind: Kind<'_>,
|
||||||
|
@ -654,7 +653,7 @@ impl<'a> TimelineQuery<'a> {
|
||||||
|
|
||||||
pub fn matches(
|
pub fn matches(
|
||||||
&self,
|
&self,
|
||||||
conn: &DbConn,
|
conn: &Connection,
|
||||||
timeline: &Timeline,
|
timeline: &Timeline,
|
||||||
post: &Post,
|
post: &Post,
|
||||||
kind: Kind<'_>,
|
kind: Kind<'_>,
|
||||||
|
|
|
@ -191,10 +191,10 @@ impl User {
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_fqn(conn: &DbConn, fqn: &str) -> Result<User> {
|
pub fn find_by_fqn(conn: &Connection, fqn: &str) -> Result<User> {
|
||||||
let from_db = users::table
|
let from_db = users::table
|
||||||
.filter(users::fqn.eq(fqn))
|
.filter(users::fqn.eq(fqn))
|
||||||
.first(&**conn)
|
.first(conn)
|
||||||
.optional()?;
|
.optional()?;
|
||||||
if let Some(from_db) = from_db {
|
if let Some(from_db) = from_db {
|
||||||
Ok(from_db)
|
Ok(from_db)
|
||||||
|
@ -219,7 +219,7 @@ impl User {
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_from_webfinger(conn: &DbConn, acct: &str) -> Result<User> {
|
fn fetch_from_webfinger(conn: &Connection, acct: &str) -> Result<User> {
|
||||||
let link = resolve(acct.to_owned(), true)?
|
let link = resolve(acct.to_owned(), true)?
|
||||||
.links
|
.links
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -921,15 +921,15 @@ impl IntoId for User {
|
||||||
|
|
||||||
impl Eq for User {}
|
impl Eq for User {}
|
||||||
|
|
||||||
impl FromId<DbConn> for User {
|
impl FromId<Connection> for User {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = CustomPerson;
|
type Object = CustomPerson;
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
fn from_db(conn: &Connection, id: &str) -> Result<Self> {
|
||||||
Self::find_by_ap_url(conn, id)
|
Self::find_by_ap_url(conn, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result<Self> {
|
fn from_activity(conn: &Connection, acct: CustomPerson) -> Result<Self> {
|
||||||
let actor = acct.ap_actor_ref();
|
let actor = acct.ap_actor_ref();
|
||||||
let username = actor
|
let username = actor
|
||||||
.preferred_username()
|
.preferred_username()
|
||||||
|
@ -1030,7 +1030,7 @@ impl FromId<DbConn> for User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsActor<&DbConn> for User {
|
impl AsActor<&Connection> for User {
|
||||||
fn get_inbox_url(&self) -> String {
|
fn get_inbox_url(&self) -> String {
|
||||||
self.inbox_url.clone()
|
self.inbox_url.clone()
|
||||||
}
|
}
|
||||||
|
@ -1046,11 +1046,11 @@ impl AsActor<&DbConn> for User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Delete, &DbConn> for User {
|
impl AsObject<User, Delete, &Connection> for User {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> {
|
fn activity(self, conn: &Connection, actor: User, _id: &str) -> Result<()> {
|
||||||
if self.id == actor.id {
|
if self.id == actor.id {
|
||||||
self.delete(conn).map(|_| ())
|
self.delete(conn).map(|_| ())
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue