Systém hlášení a vizuální změny

Tento commit je obsažen v:
sneedmaster 2024-02-17 22:24:39 +01:00
rodič 72fd48d11e
revize a453130797
39 změnil soubory, kde provedl 511 přidání a 216 odebrání

217
Cargo.lock vygenerováno
Zobrazit soubor

@ -327,6 +327,54 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "anstream"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
[[package]]
name = "anstyle-parse"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.74" version = "1.0.74"
@ -583,7 +631,7 @@ dependencies = [
"pure-rust-locales", "pure-rust-locales",
"serde", "serde",
"wasm-bindgen", "wasm-bindgen",
"windows-targets", "windows-targets 0.48.1",
] ]
[[package]] [[package]]
@ -623,6 +671,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]] [[package]]
name = "combine" name = "combine"
version = "4.6.6" version = "4.6.6"
@ -1004,16 +1058,26 @@ dependencies = [
] ]
[[package]] [[package]]
name = "env_logger" name = "env_filter"
version = "0.10.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
dependencies = [ dependencies = [
"humantime",
"is-terminal",
"log", "log",
"regex", "regex",
"termcolor", ]
[[package]]
name = "env_logger"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c012a26a7f605efc424dd53697843a72be7dc86ad2d01f7814337794a12231d"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime",
"log",
] ]
[[package]] [[package]]
@ -1030,7 +1094,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
dependencies = [ dependencies = [
"errno-dragonfly", "errno-dragonfly",
"libc", "libc",
"windows-sys", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@ -1051,7 +1115,7 @@ checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"home", "home",
"windows-sys", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@ -1378,7 +1442,7 @@ version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
dependencies = [ dependencies = [
"windows-sys", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@ -1539,17 +1603,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "is-terminal"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi",
"rustix",
"windows-sys",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.10.5" version = "0.10.5"
@ -1785,7 +1838,7 @@ dependencies = [
"libc", "libc",
"log", "log",
"wasi", "wasi",
"windows-sys", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@ -1967,7 +2020,7 @@ dependencies = [
"libc", "libc",
"redox_syscall", "redox_syscall",
"smallvec", "smallvec",
"windows-targets", "windows-targets 0.48.1",
] ]
[[package]] [[package]]
@ -2313,7 +2366,7 @@ dependencies = [
"libc", "libc",
"spin 0.9.8", "spin 0.9.8",
"untrusted", "untrusted",
"windows-sys", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@ -2363,7 +2416,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@ -2595,7 +2648,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@ -2889,16 +2942,7 @@ dependencies = [
"fastrand", "fastrand",
"redox_syscall", "redox_syscall",
"rustix", "rustix",
"windows-sys", "windows-sys 0.48.0",
]
[[package]]
name = "termcolor"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
] ]
[[package]] [[package]]
@ -2990,7 +3034,7 @@ dependencies = [
"signal-hook-registry", "signal-hook-registry",
"socket2 0.5.3", "socket2 0.5.3",
"tokio-macros", "tokio-macros",
"windows-sys", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@ -3167,6 +3211,12 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1" checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"
@ -3267,15 +3317,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
@ -3288,7 +3329,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [ dependencies = [
"windows-targets", "windows-targets 0.48.1",
] ]
[[package]] [[package]]
@ -3297,7 +3338,16 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [ dependencies = [
"windows-targets", "windows-targets 0.48.1",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.0",
] ]
[[package]] [[package]]
@ -3306,13 +3356,28 @@ version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm", "windows_aarch64_gnullvm 0.48.0",
"windows_aarch64_msvc", "windows_aarch64_msvc 0.48.0",
"windows_i686_gnu", "windows_i686_gnu 0.48.0",
"windows_i686_msvc", "windows_i686_msvc 0.48.0",
"windows_x86_64_gnu", "windows_x86_64_gnu 0.48.0",
"windows_x86_64_gnullvm", "windows_x86_64_gnullvm 0.48.0",
"windows_x86_64_msvc", "windows_x86_64_msvc 0.48.0",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
] ]
[[package]] [[package]]
@ -3321,42 +3386,84 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.5.18" version = "0.5.18"

Zobrazit soubor

@ -15,7 +15,7 @@ chrono-tz = "0.8.5"
dotenv = "0.15.0" dotenv = "0.15.0"
enumflags2 = "0.7.7" enumflags2 = "0.7.7"
encoding = "0.2.33" encoding = "0.2.33"
env_logger = "0.10.0" env_logger = "0.11.2"
glob = "0.3.1" glob = "0.3.1"
image = "0.24.7" image = "0.24.7"
ipnetwork = "0.20.0" ipnetwork = "0.20.0"

Zobrazit soubor

@ -64,6 +64,7 @@ pub struct BoardCfg {
pub flags: bool, pub flags: bool,
pub thread_captcha: String, pub thread_captcha: String,
pub reply_captcha: String, pub reply_captcha: String,
pub board_theme: String,
pub require_thread_content: bool, pub require_thread_content: bool,
pub require_thread_file: bool, pub require_thread_file: bool,
pub require_reply_content: bool, pub require_reply_content: bool,

Zobrazit soubor

@ -5,7 +5,7 @@ use sqlx::{query, query_as, types::Json};
use std::net::IpAddr; use std::net::IpAddr;
use super::models::{Board, File, Post, Report}; use super::models::{Board, File, Post, Report};
use crate::{ctx::Ctx, error::NekrochanError}; use crate::{ctx::Ctx, error::NekrochanError, GENERIC_PAGE_SIZE};
impl Post { impl Post {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -161,8 +161,8 @@ impl Post {
LIMIT $1 LIMIT $1
OFFSET $2"#, OFFSET $2"#,
) )
.bind(15) .bind(GENERIC_PAGE_SIZE)
.bind((page - 1) * 15) .bind((page - 1) * GENERIC_PAGE_SIZE)
.fetch_all(ctx.db()) .fetch_all(ctx.db())
.await?; .await?;
@ -181,12 +181,16 @@ impl Post {
Ok(posts) Ok(posts)
} }
pub async fn read_reports(ctx: &Ctx) -> Result<Vec<Self>, NekrochanError> { pub async fn read_reports_page(ctx: &Ctx, page: i64) -> Result<Vec<Self>, NekrochanError> {
let posts = query_as( let posts = query_as(
r#"SELECT * FROM overboard r#"SELECT * FROM overboard
WHERE reports != '[]'::jsonb WHERE reports != '[]'::jsonb
ORDER BY jsonb_array_length(reports), reported DESC"#, ORDER BY jsonb_array_length(reports), reported DESC
LIMIT $1
OFFSET $2"#,
) )
.bind(GENERIC_PAGE_SIZE)
.bind((page - 1) * GENERIC_PAGE_SIZE)
.fetch_all(ctx.db()) .fetch_all(ctx.db())
.await?; .await?;
@ -195,7 +199,7 @@ impl Post {
pub async fn read_replies(&self, ctx: &Ctx) -> Result<Vec<Self>, NekrochanError> { pub async fn read_replies(&self, ctx: &Ctx) -> Result<Vec<Self>, NekrochanError> {
let replies = query_as(&format!( let replies = query_as(&format!(
"SELECT * FROM posts_{} WHERE thread = $1 ORDER BY created ASC", "SELECT * FROM posts_{} WHERE thread = $1 ORDER BY sticky DESC, created ASC",
self.board self.board
)) ))
.bind(self.id) .bind(self.id)

Zobrazit soubor

@ -7,6 +7,8 @@ lazy_static! {
RwLock::new(HashMap::new()); RwLock::new(HashMap::new());
} }
const GENERIC_PAGE_SIZE: i64 = 15;
pub mod auth; pub mod auth;
pub mod cfg; pub mod cfg;
pub mod ctx; pub mod ctx;
@ -22,7 +24,13 @@ pub mod trip;
pub mod web; pub mod web;
pub fn paginate(page_size: i64, count: i64) -> i64 { pub fn paginate(page_size: i64, count: i64) -> i64 {
count / page_size + (count % page_size).signum() let pages = count / page_size + (count % page_size).signum();
if pages == 0 {
return 1;
} else {
return pages;
}
} }
pub fn check_page( pub fn check_page(

Zobrazit soubor

@ -26,7 +26,7 @@ pub async fn edit_posts(
let ids = edits.keys().map(|s| s.to_owned()).collect::<Vec<String>>(); let ids = edits.keys().map(|s| s.to_owned()).collect::<Vec<String>>();
let posts = get_posts_from_ids(&ctx, ids) let posts = get_posts_from_ids(&ctx, &ids)
.await .await
.into_iter() .into_iter()
.map(|post| (format!("{}/{}", post.board, post.id), post)) .map(|post| (format!("{}/{}", post.board, post.id), post))

Zobrazit soubor

@ -17,7 +17,7 @@ pub struct ActionTemplate {
pub response: String, pub response: String,
} }
pub async fn get_posts_from_ids(ctx: &Ctx, ids: Vec<String>) -> Vec<Post> { pub async fn get_posts_from_ids(ctx: &Ctx, ids: &Vec<String>) -> Vec<Post> {
let mut posts = Vec::new(); let mut posts = Vec::new();
for id in ids { for id in ids {

Zobrazit soubor

@ -39,7 +39,7 @@ pub async fn report_posts(
} }
let boards = Board::read_all_map(&ctx).await?; let boards = Board::read_all_map(&ctx).await?;
let posts = get_posts_from_ids(&ctx, form.posts).await; let posts = get_posts_from_ids(&ctx, &form.posts).await;
let mut response = String::new(); let mut response = String::new();
let mut posts_reported = 0; let mut posts_reported = 0;

Zobrazit soubor

@ -2,11 +2,11 @@ use actix_web::{post, web::Data, HttpRequest, HttpResponse};
use chrono::{Duration, Utc}; use chrono::{Duration, Utc};
use ipnetwork::IpNetwork; use ipnetwork::IpNetwork;
use serde::Deserialize; use serde::Deserialize;
use std::{collections::HashSet, fmt::Write}; use std::{collections::HashSet, fmt::Write, net::IpAddr};
use crate::{ use crate::{
ctx::Ctx, ctx::Ctx,
db::models::Ban, db::models::{Account, Ban},
error::NekrochanError, error::NekrochanError,
qsform::QsForm, qsform::QsForm,
web::{ web::{
@ -28,11 +28,13 @@ pub struct StaffPostActionsForm {
pub toggle_spoiler: Option<String>, pub toggle_spoiler: Option<String>,
pub toggle_sticky: Option<String>, pub toggle_sticky: Option<String>,
pub toggle_lock: Option<String>, pub toggle_lock: Option<String>,
pub remove_reports: Option<String>,
pub ban_user: Option<String>, pub ban_user: Option<String>,
pub ban_reporters: Option<String>,
pub global_ban: Option<String>, pub global_ban: Option<String>,
pub unappealable_ban: Option<String>, pub unappealable_ban: Option<String>,
pub ban_reason: Option<String>, pub ban_reason: Option<String>,
pub ban_duration: Option<i64>, pub ban_duration: Option<u64>,
pub ban_range: Option<String>, pub ban_range: Option<String>,
} }
@ -44,7 +46,7 @@ pub async fn staff_post_actions(
) -> Result<HttpResponse, NekrochanError> { ) -> Result<HttpResponse, NekrochanError> {
let tcx = TemplateCtx::new(&ctx, &req).await?; let tcx = TemplateCtx::new(&ctx, &req).await?;
let account = account_from_auth(&ctx, &req).await?; let account = account_from_auth(&ctx, &req).await?;
let posts = get_posts_from_ids(&ctx, form.posts).await; let posts = get_posts_from_ids(&ctx, &form.posts).await;
let mut response = String::new(); let mut response = String::new();
@ -53,6 +55,7 @@ pub async fn staff_post_actions(
let mut spoilers_toggled = 0; let mut spoilers_toggled = 0;
let mut stickies_toggled = 0; let mut stickies_toggled = 0;
let mut locks_toggled = 0; let mut locks_toggled = 0;
let mut reports_removed = 0;
let mut bans_issued = 0; let mut bans_issued = 0;
for post in &posts { for post in &posts {
@ -89,20 +92,30 @@ pub async fn staff_post_actions(
if form.toggle_sticky.is_some() { if form.toggle_sticky.is_some() {
post.update_sticky(&ctx).await?; post.update_sticky(&ctx).await?;
stickies_toggled += post.files.0.len(); stickies_toggled += 1;
} }
if form.toggle_lock.is_some() { if form.toggle_lock.is_some() {
post.update_lock(&ctx).await?; post.update_lock(&ctx).await?;
locks_toggled += post.files.0.len(); locks_toggled += 1;
}
if form.remove_reports.is_some() {
post.delete_reports(&ctx).await?;
reports_removed += post.reports.0.len();
} }
} }
let mut already_banned = HashSet::new(); let mut already_banned = HashSet::new();
for post in posts { for post in posts {
if let (Some(_), Some(ban_reason), Some(ban_duration), Some(ban_range)) = ( if let (
form.ban_user.clone(), (Some(_), None) | (None, Some(_)) | (Some(_), Some(_)),
Some(reason),
Some(duration),
Some(range),
) = (
(form.ban_user.clone(), form.ban_reporters.clone()),
form.ban_reason.clone(), form.ban_reason.clone(),
form.ban_duration, form.ban_duration,
form.ban_range.clone(), form.ban_range.clone(),
@ -112,63 +125,56 @@ pub async fn staff_post_actions(
continue; continue;
} }
if already_banned.contains(&post.ip) { let mut ips_to_ban = HashSet::new();
if form.ban_user.is_some() && !already_banned.contains(&post.ip) {
ips_to_ban.insert(post.ip);
}
if form.ban_reporters.is_some() {
ips_to_ban.extend(
post.reports
.0
.iter()
.map(|r| r.reporter_ip)
.filter(|ip| !already_banned.contains(ip)),
)
}
if ips_to_ban.is_empty() {
continue; continue;
} }
let account = account.username.clone(); for ip in &ips_to_ban {
ban_ip(
let board = if form.global_ban.is_none() { &ctx,
Some(post.board.clone()) &account,
} else { &form,
None *ip,
}; post.board.clone(),
reason.clone(),
let prefix = if post.ip.is_ipv4() { duration,
match ban_range.as_str() { &range,
"lan" => 24, )
"isp" => 16, .await?;
_ => 32,
}
} else {
match ban_range.as_str() {
"lan" => 48,
"isp" => 24,
_ => 128,
}
};
let ip_range = IpNetwork::new(post.ip, prefix)?;
let reason: String = ban_reason.trim().into();
if reason.is_empty() || reason.len() > 200 {
return Err(NekrochanError::BanReasonFormatError);
} }
let appealable = form.unappealable_ban.is_none(); if form.ban_user.is_some() {
let content_nomarkup = format!(
"{}\n\n##(UŽIVATEL BYL ZA TENTO PŘÍSPĚVEK ZABANOVÁN)##",
post.content_nomarkup
);
let expires = if ban_duration == 0 { let content = format!(
None "{}\n\n<span class=\"jannytext\">(UŽIVATEL BYL ZA TENTO PŘÍSPĚVEK ZABANOVÁN)</span>",
} else { post.content
Some(Utc::now() + Duration::days(ban_duration)) );
};
Ban::create(&ctx, account, board, ip_range, reason, appealable, expires).await?; post.update_content(&ctx, content, content_nomarkup).await?;
}
let content_nomarkup = format!( bans_issued += ips_to_ban.len();
"{}\n\n##(UŽIVATEL BYL ZA TENTO PŘÍSPĚVEK ZABANOVÁN)##", already_banned.extend(ips_to_ban);
post.content_nomarkup
);
let content = format!(
"{}\n\n<span class=\"jannytext\">(UŽIVATEL BYL ZA TENTO PŘÍSPĚVEK ZABANOVÁN)</span>",
post.content
);
post.update_content(&ctx, content, content_nomarkup).await?;
already_banned.insert(post.ip);
bans_issued += 1;
} }
} }
@ -199,7 +205,7 @@ pub async fn staff_post_actions(
if stickies_toggled != 0 { if stickies_toggled != 0 {
writeln!( writeln!(
&mut response, &mut response,
"[Úspěch] Připnuta/odepnuta vlákna: {stickies_toggled}" "[Úspěch] Připnuty/odepnuty příspěvky: {stickies_toggled}"
) )
.ok(); .ok();
} }
@ -212,6 +218,14 @@ pub async fn staff_post_actions(
.ok(); .ok();
} }
if reports_removed != 0 {
writeln!(
&mut response,
"[Úspěch] Odstraněna hlášení: {reports_removed}"
)
.ok();
}
if bans_issued != 0 { if bans_issued != 0 {
writeln!(&mut response, "[Úspěch] Uděleny bany: {bans_issued}").ok(); writeln!(&mut response, "[Úspěch] Uděleny bany: {bans_issued}").ok();
} }
@ -220,3 +234,55 @@ pub async fn staff_post_actions(
template_response(&template) template_response(&template)
} }
async fn ban_ip(
ctx: &Ctx,
account: &Account,
form: &StaffPostActionsForm,
ip: IpAddr,
board: String,
reason: String,
duration: u64,
range: &str,
) -> Result<(), NekrochanError> {
let account = account.username.clone();
let board = if form.global_ban.is_none() {
Some(board)
} else {
None
};
let prefix = if ip.is_ipv4() {
match range {
"lan" => 24,
"isp" => 16,
_ => 32,
}
} else {
match range {
"lan" => 48,
"isp" => 24,
_ => 128,
}
};
let ip_range = IpNetwork::new(ip, prefix)?;
let reason: String = reason.trim().into();
if reason.is_empty() || reason.len() > 200 {
return Err(NekrochanError::BanReasonFormatError);
}
let appealable = form.unappealable_ban.is_none();
let expires = if duration == 0 {
None
} else {
Some(Utc::now() + Duration::days(duration as i64))
};
Ban::create(&ctx, account, board, ip_range, reason, appealable, expires).await?;
Ok(())
}

Zobrazit soubor

@ -42,7 +42,7 @@ pub async fn user_post_actions(
} }
} }
let posts = get_posts_from_ids(&ctx, form.posts).await; let posts = get_posts_from_ids(&ctx, &form.posts).await;
let boards = Board::read_all_map(&ctx).await?; let boards = Board::read_all_map(&ctx).await?;
let mut response = String::new(); let mut response = String::new();

Zobrazit soubor

@ -35,7 +35,7 @@ pub async fn edit_posts(
return Err(NekrochanError::InsufficientPermissionError); return Err(NekrochanError::InsufficientPermissionError);
} }
let posts = get_posts_from_ids(&ctx, form.posts).await; let posts = get_posts_from_ids(&ctx, &form.posts).await;
let template = EditPostsTemplate { tcx, posts }; let template = EditPostsTemplate { tcx, posts };
template_response(&template) template_response(&template)

Zobrazit soubor

@ -9,12 +9,7 @@ use serde::Deserialize;
use std::collections::HashMap; use std::collections::HashMap;
use crate::{ use crate::{
check_page, check_page, ctx::Ctx, db::models::{Board, Post}, error::NekrochanError, filters, paginate, web::{tcx::TemplateCtx, template_response}, GENERIC_PAGE_SIZE
ctx::Ctx,
db::models::{Board, Post},
error::NekrochanError,
filters, paginate,
web::{tcx::TemplateCtx, template_response},
}; };
#[derive(Deserialize)] #[derive(Deserialize)]
@ -43,7 +38,7 @@ pub async fn overboard(
let count = ctx.cache().get("total_threads").await?; let count = ctx.cache().get("total_threads").await?;
let page = query.map_or(1, |q| q.page); let page = query.map_or(1, |q| q.page);
let pages = paginate(15, count); let pages = paginate(GENERIC_PAGE_SIZE, count);
check_page(page, pages, None)?; check_page(page, pages, None)?;

Zobrazit soubor

@ -19,6 +19,11 @@ struct AccountsTemplate {
#[get("/staff/accounts")] #[get("/staff/accounts")]
pub async fn accounts(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> { pub async fn accounts(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> {
let tcx = TemplateCtx::new(&ctx, &req).await?; let tcx = TemplateCtx::new(&ctx, &req).await?;
if tcx.account.is_none() {
return Err(NekrochanError::NotLoggedInError);
}
let accounts = Account::read_all(&ctx).await?; let accounts = Account::read_all(&ctx).await?;
let template = AccountsTemplate { tcx, accounts }; let template = AccountsTemplate { tcx, accounts };

Zobrazit soubor

@ -20,6 +20,7 @@ pub struct UpdateBoardConfigForm {
flags: Option<String>, flags: Option<String>,
thread_captcha: String, thread_captcha: String,
reply_captcha: String, reply_captcha: String,
board_theme: String,
require_thread_content: Option<String>, require_thread_content: Option<String>,
require_thread_file: Option<String>, require_thread_file: Option<String>,
require_reply_content: Option<String>, require_reply_content: Option<String>,
@ -58,6 +59,7 @@ pub async fn update_board_config(
let flags = form.flags.is_some(); let flags = form.flags.is_some();
let thread_captcha = form.thread_captcha; let thread_captcha = form.thread_captcha;
let reply_captcha = form.reply_captcha; let reply_captcha = form.reply_captcha;
let board_theme = form.board_theme;
let require_thread_content = form.require_thread_content.is_some(); let require_thread_content = form.require_thread_content.is_some();
let require_thread_file = form.require_thread_file.is_some(); let require_thread_file = form.require_thread_file.is_some();
let require_reply_content = form.require_reply_content.is_some(); let require_reply_content = form.require_reply_content.is_some();
@ -79,6 +81,7 @@ pub async fn update_board_config(
flags, flags,
thread_captcha, thread_captcha,
reply_captcha, reply_captcha,
board_theme,
require_thread_content, require_thread_content,
require_thread_file, require_thread_file,
require_reply_content, require_reply_content,

Zobrazit soubor

@ -5,10 +5,7 @@ use crate::{
ctx::Ctx, ctx::Ctx,
db::models::Banner, db::models::Banner,
error::NekrochanError, error::NekrochanError,
web::{ web::{tcx::TemplateCtx, template_response},
tcx::{account_from_auth, TemplateCtx},
template_response,
},
}; };
#[derive(Template)] #[derive(Template)]
@ -21,9 +18,8 @@ struct BannersTemplate {
#[get("/staff/banners")] #[get("/staff/banners")]
pub async fn banners(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> { pub async fn banners(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> {
let tcx = TemplateCtx::new(&ctx, &req).await?; let tcx = TemplateCtx::new(&ctx, &req).await?;
let account = account_from_auth(&ctx, &req).await?;
if !(account.perms().owner() || account.perms().banners()) { if !(tcx.perms.owner() || tcx.perms.banners()) {
return Err(NekrochanError::InsufficientPermissionError); return Err(NekrochanError::InsufficientPermissionError);
} }

Zobrazit soubor

@ -6,10 +6,7 @@ use crate::{
db::models::Ban, db::models::Ban,
error::NekrochanError, error::NekrochanError,
filters, filters,
web::{ web::{tcx::TemplateCtx, template_response},
tcx::{account_from_auth, TemplateCtx},
template_response,
},
}; };
#[derive(Template)] #[derive(Template)]
@ -22,9 +19,8 @@ struct BansTemplate {
#[get("/staff/bans")] #[get("/staff/bans")]
pub async fn bans(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> { pub async fn bans(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> {
let tcx = TemplateCtx::new(&ctx, &req).await?; let tcx = TemplateCtx::new(&ctx, &req).await?;
let account = account_from_auth(&ctx, &req).await?;
if !(account.perms().owner() || account.perms().bans()) { if !(tcx.perms.owner() || tcx.perms.bans()) {
return Err(NekrochanError::InsufficientPermissionError); return Err(NekrochanError::InsufficientPermissionError);
} }

Zobrazit soubor

@ -9,10 +9,7 @@ use crate::{
ctx::Ctx, ctx::Ctx,
db::models::Board, db::models::Board,
error::NekrochanError, error::NekrochanError,
web::{ web::{tcx::TemplateCtx, template_response},
tcx::{account_from_auth, TemplateCtx},
template_response,
},
}; };
#[derive(Template)] #[derive(Template)]
@ -29,9 +26,8 @@ pub async fn board_config(
board: Path<String>, board: Path<String>,
) -> Result<HttpResponse, NekrochanError> { ) -> Result<HttpResponse, NekrochanError> {
let tcx = TemplateCtx::new(&ctx, &req).await?; let tcx = TemplateCtx::new(&ctx, &req).await?;
let account = account_from_auth(&ctx, &req).await?;
if !(account.perms().owner() || account.perms().board_config()) { if !(tcx.perms.owner() || tcx.perms.board_config()) {
return Err(NekrochanError::InsufficientPermissionError); return Err(NekrochanError::InsufficientPermissionError);
} }

Zobrazit soubor

@ -6,10 +6,7 @@ use crate::{
db::models::Board, db::models::Board,
error::NekrochanError, error::NekrochanError,
filters, filters,
web::{ web::{tcx::TemplateCtx, template_response},
tcx::{account_from_auth, TemplateCtx},
template_response,
},
}; };
#[derive(Template)] #[derive(Template)]
@ -22,9 +19,8 @@ struct BoardsTemplate {
#[get("/staff/boards")] #[get("/staff/boards")]
pub async fn boards(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> { pub async fn boards(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> {
let tcx = TemplateCtx::new(&ctx, &req).await?; let tcx = TemplateCtx::new(&ctx, &req).await?;
let account = account_from_auth(&ctx, &req).await?;
if !(account.perms().owner() || account.perms().board_config() || account.perms().banners()) { if !(tcx.perms.owner() || tcx.perms.board_config() || tcx.perms.banners()) {
return Err(NekrochanError::InsufficientPermissionError); return Err(NekrochanError::InsufficientPermissionError);
} }

Zobrazit soubor

@ -18,6 +18,11 @@ struct NewsTemplate {
#[get("/staff/news")] #[get("/staff/news")]
pub async fn news(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> { pub async fn news(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> {
let tcx = TemplateCtx::new(&ctx, &req).await?; let tcx = TemplateCtx::new(&ctx, &req).await?;
if !(tcx.perms.owner() || tcx.perms.news()) {
return Err(NekrochanError::InsufficientPermissionError);
}
let news = NewsPost::read_all(&ctx).await?; let news = NewsPost::read_all(&ctx).await?;
let template = NewsTemplate { tcx, news }; let template = NewsTemplate { tcx, news };

Zobrazit soubor

@ -9,10 +9,7 @@ use crate::{
ctx::Ctx, ctx::Ctx,
db::models::Account, db::models::Account,
error::NekrochanError, error::NekrochanError,
web::{ web::{tcx::TemplateCtx, template_response},
tcx::{account_from_auth, TemplateCtx},
template_response,
},
}; };
#[derive(Template)] #[derive(Template)]
@ -29,7 +26,10 @@ pub async fn permissions(
path: Path<String>, path: Path<String>,
) -> Result<HttpResponse, NekrochanError> { ) -> Result<HttpResponse, NekrochanError> {
let tcx = TemplateCtx::new(&ctx, &req).await?; let tcx = TemplateCtx::new(&ctx, &req).await?;
let _ = account_from_auth(&ctx, &req).await?;
if !tcx.perms.owner() {
return Err(NekrochanError::InsufficientPermissionError);
}
let account = path.into_inner(); let account = path.into_inner();
let account = Account::read(&ctx, account.clone()) let account = Account::read(&ctx, account.clone())

Zobrazit soubor

@ -1,23 +1,63 @@
use actix_web::{get, web::Data, HttpRequest, HttpResponse}; use std::collections::HashMap;
use actix_web::{
get,
web::{Data, Query},
HttpRequest, HttpResponse,
};
use askama::Template; use askama::Template;
use serde::Deserialize;
use crate::{ use crate::{
ctx::Ctx, ctx::Ctx,
db::models::{Board, Post},
error::NekrochanError, error::NekrochanError,
filters,
web::{tcx::TemplateCtx, template_response}, web::{tcx::TemplateCtx, template_response},
}; };
#[derive(Deserialize)]
pub struct BoardQuery {
page: i64,
}
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Template)] #[derive(Template)]
#[template(path = "staff/reports.html")] #[template(path = "staff/reports.html")]
struct ReportsTemplate { struct ReportsTemplate {
tcx: TemplateCtx, tcx: TemplateCtx,
boards: HashMap<String, Board>,
posts: Vec<Post>,
page: i64,
} }
#[get("/staff/reports")] #[get("/staff/reports")]
async fn reports(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> { async fn reports(
ctx: Data<Ctx>,
req: HttpRequest,
query: Option<Query<BoardQuery>>,
) -> Result<HttpResponse, NekrochanError> {
let tcx = TemplateCtx::new(&ctx, &req).await?; let tcx = TemplateCtx::new(&ctx, &req).await?;
let template = ReportsTemplate { tcx };
if !(tcx.perms.owner() || tcx.perms.reports()) {
return Err(NekrochanError::InsufficientPermissionError);
}
let boards = Board::read_all_map(&ctx).await?;
let page = query.map_or(1, |q| q.page);
if page <= 0 {
return Err(NekrochanError::InvalidPageError);
}
let posts = Post::read_reports_page(&ctx, page).await?;
let template = ReportsTemplate {
tcx,
boards,
posts,
page,
};
template_response(&template) template_response(&template)
} }

Zobrazit soubor

@ -90,7 +90,6 @@ summary {
resize: none; resize: none;
} }
.reply-mode { .reply-mode {
font-weight: bold; font-weight: bold;
font-size: 18px; font-size: 18px;
@ -105,26 +104,25 @@ summary {
} }
.table-wrap { .table-wrap {
overflow-x: auto; overflow: scroll;
} }
.data-table { .data-table {
width: 100%; width: 100%;
background-color: var(--table-primary);
border-spacing: 0; border-spacing: 0;
border-collapse: collapse; border-collapse: collapse;
margin: 8px 0; margin: 8px 0;
} }
.data-table tr:nth-child(2n + 1) {
background-color: var(--table-secondary);
}
.data-table th { .data-table th {
background-color: var(--table-head); background-color: var(--table-head);
} }
.data-table td, .data-table td {
background-color: var(--table-background);
}
.data-table td:not(.form-table td),
.data-table th { .data-table th {
border: 1px solid var(--table-border); border: 1px solid var(--table-border);
padding: 4px; padding: 4px;
@ -213,6 +211,10 @@ summary {
table-layout: fixed; table-layout: fixed;
} }
.m-0 {
margin: 0;
}
.form-table .button, .form-table .button,
.full-width { .full-width {
display: block; display: block;
@ -238,8 +240,9 @@ summary {
clear: both; clear: both;
} }
.board-links { .board-links,
color: var(--board-links-color); .pagination {
color: var(--link-list-color);
} }
.link-separator::after { .link-separator::after {
@ -432,8 +435,8 @@ summary {
} }
.icon { .icon {
height: 1em; height: 0.8em;
vertical-align: text-top; vertical-align: middle;
} }
.posts-omitted { .posts-omitted {

Zobrazit soubor

@ -8,7 +8,7 @@
--link-hover: #dd0000; --link-hover: #dd0000;
--post-link-color: #34345c; --post-link-color: #34345c;
--post-link-hover: #dd0000; --post-link-hover: #dd0000;
--board-links-color: #8899aa; --link-list-color: #8899aa;
--title-color: #af0a0f; --title-color: #af0a0f;
--title-font: tahoma; --title-font: tahoma;
--hr-color: #d3d3d3; --hr-color: #d3d3d3;
@ -18,8 +18,7 @@
/* Tables */ /* Tables */
--table-head: #9988ee; --table-head: #9988ee;
--table-border: #000000; --table-border: #000000;
--table-primary: #ffffff; --table-background: #ffffff;
--table-secondary: #d6daf0;
/* Forms */ /* Forms */
--input-color: #ffffff; --input-color: #ffffff;
--input-border: #808080; --input-border: #808080;

Zobrazit soubor

@ -8,7 +8,7 @@
--link-hover: #dd0000; --link-hover: #dd0000;
--post-link-color: #000080; --post-link-color: #000080;
--post-link-hover: #dd0000; --post-link-hover: #dd0000;
--board-links-color: #bb8866; --link-list-color: #bb8866;
--title-color: #af0a0f; --title-color: #af0a0f;
--title-font: tahoma; --title-font: tahoma;
--hr-color: #d9bfb7; --hr-color: #d9bfb7;
@ -18,8 +18,7 @@
/* Tables */ /* Tables */
--table-head: #ffccaa; --table-head: #ffccaa;
--table-border: #800000; --table-border: #800000;
--table-primary: #ffffff; --table-background: #ffffff;
--table-secondary: #ffffff;
/* Forms */ /* Forms */
--input-color: #ffffff; --input-color: #ffffff;
--input-border: #808080; --input-border: #808080;

Zobrazit soubor

@ -4,7 +4,7 @@
{% block content %} {% block content %}
<div class="container"> <div class="container">
<h1 class="title">Jsi trans, btw.</h1> <h1 class="title">Zabanován!!!</h1>
<table class="data-table"> <table class="data-table">
<tr><th>Máš ban!</th></tr> <tr><th>Máš ban!</th></tr>
<tr> <tr>
@ -20,7 +20,7 @@
&#32;z následujícího důvodu: &#32;z následujícího důvodu:
</b> </b>
<div class="post-content"> <div class="post-content box">
{{ ban.reason }} {{ ban.reason }}
</div> </div>
<span>Udělil <i>{{ ban.issued_by }}</i></span> <span>Udělil <i>{{ ban.issued_by }}</i></span>
@ -45,7 +45,7 @@
{% else %} {% else %}
<b>Můžeš se pokusit svůj ban odvolat:</b> <b>Můžeš se pokusit svůj ban odvolat:</b>
<form method="post" action="/actions/appeal-ban"> <form method="post" action="/actions/appeal-ban">
<input name="id" type="hidden" value="{{ ban.id }}" /> <input name="id" type="hidden" value="{{ ban.id }}">
<table class="form-table"> <table class="form-table">
<tr> <tr>
<td class="label">Odvolání</td> <td class="label">Odvolání</td>
@ -62,6 +62,6 @@
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
</div> </table>
</div> </div>
{% endblock %} {% endblock %}

Zobrazit soubor

@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{% endblock %}</title> <title>{% block title %}{% endblock %}</title>
<meta name="description" content="{{ tcx.cfg.site.description }}"> <meta name="description" content="{{ tcx.cfg.site.description }}">
<link rel="stylesheet" href="/static/themes/yotsuba.css"> <link rel="stylesheet" href='/static/themes/{% block theme %}{% include "../theme.txt" %}{% endblock %}'>
<link rel="stylesheet" href="/static/style.css"> <link rel="stylesheet" href="/static/style.css">
<!-- Muh flash of unstyled content --> <!-- Muh flash of unstyled content -->
<script>0</script> <script>0</script>

Zobrazit soubor

@ -3,6 +3,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block theme %}{{ board.config.0.board_theme }}{% endblock %}
{% block title %}Katalog (/{{ board.id }}/){% endblock %} {% block title %}Katalog (/{{ board.id }}/){% endblock %}
{% block content %} {% block content %}

Zobrazit soubor

@ -5,6 +5,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block theme %}{{ board.config.0.board_theme }}{% endblock %}
{% block title %}/{{ board.id }}/ - {{ board.name }}{% endblock %} {% block title %}/{{ board.id }}/ - {{ board.name }}{% endblock %}
{% block content %} {% block content %}

Zobrazit soubor

@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chyba</title> <title>Chyba</title>
<link rel="stylesheet" href="/static/themes/yotsuba.css"> <link rel="stylesheet" href='/static/themes/{% include "../theme.txt" %}'>
<link rel="stylesheet" href="/static/style.css"> <link rel="stylesheet" href="/static/style.css">
</head> </head>
<body> <body>

Zobrazit soubor

@ -1,15 +1,25 @@
{% macro pagination(base, pages, current) %} {% macro pagination(base, pages, current) %}
<div class="box inline-block pagination"> <div class="box inline-block pagination">
{% if pages == 0 %} {% if current == 1 %}
<b><a href="{{ base }}?page=1">[1]</a></b>&#32; [Předchozí]
{% else %} {% else %}
{% for page in 1..(pages + 1) %} [<a href="{{ base }}?page={{ current - 1 }}">Předchozí</a>]
{% if page == current %} {% endif %}
<b><a href="{{ base }}?page={{ page }}">[{{ page }}]</a></b>&#32; &#32;
{% else %}
<a href="{{ base }}?page={{ page }}">[{{ page }}]</a>&#32; {% for page in 1..(pages + 1) %}
{% endif %} {% if page == current %}
{% endfor %} [<b><a href="{{ base }}?page={{ page }}">{{ page }}</a></b>]
{% else %}
[<a href="{{ base }}?page={{ page }}">{{ page }}</a>]
{% endif %}
{% endfor %}
&#32;
{% if current == pages %}
[Další]
{% else %}
[<a href="{{ base }}?page={{ current + 1 }}">Další</a>]
{% endif %} {% endif %}
</div> </div>
{% endmacro %} {% endmacro %}

Zobrazit soubor

@ -35,7 +35,6 @@
type="text" type="text"
autocomplete="new-password" autocomplete="new-password"
value="{{ tcx.password }}" value="{{ tcx.password }}"
required=""
> >
</td> </td>
</tr> </tr>
@ -53,7 +52,7 @@
<table class="form-table"> <table class="form-table">
<tr> <tr>
<td class="label">Důvod hlášení</td> <td class="label">Důvod hlášení</td>
<td><textarea name="report_reason"></textarea></td> <td><input name="report_reason" type="text"></td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
@ -114,6 +113,16 @@
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
{% if tcx.perms.owner() || tcx.perms.reports() %}
<tr>
<td class="label">Odstranit hlášení</td>
<td>
<div class="input-wrapper">
<input name="remove_reports" type="checkbox">
</div>
</td>
</tr>
{% endif %}
{% if tcx.perms.owner() || tcx.perms.bans() %} {% if tcx.perms.owner() || tcx.perms.bans() %}
<tr> <tr>
<td class="label">Zabanovat uživatele</td> <td class="label">Zabanovat uživatele</td>
@ -123,6 +132,16 @@
</div> </div>
</td> </td>
</tr> </tr>
{% if tcx.perms.owner() || tcx.perms.reports() %}
<tr>
<td class="label">Zabanovat nahlašovatele</td>
<td>
<div class="input-wrapper">
<input name="ban_reporters" type="checkbox">
</div>
</td>
</tr>
{% endif %}
<tr> <tr>
<td class="label">Globální ban</td> <td class="label">Globální ban</td>
<td> <td>
@ -144,8 +163,8 @@
<td><textarea name="ban_reason"></textarea></td> <td><textarea name="ban_reason"></textarea></td>
</tr> </tr>
<tr> <tr>
<td class="label">Délka banu (dny)</td> <td class="label">Délka banu <span class="small">(dny, 0 = trvalý)</span></td>
<td><input name="ban_duration" type="number" min="0" placeholder="0 = trvalý ban"></td> <td><input name="ban_duration" type="number" min="0" value="0"></td>
</tr> </tr>
<tr> <tr>
<td class="label">Rozsah banu</td> <td class="label">Rozsah banu</td>

Zobrazit soubor

@ -44,7 +44,7 @@
{% if file.spoiler %} {% if file.spoiler %}
[Spoiler] [Spoiler]
{% else %} {% else %}
{{ file.original_name }} {{ file.original_name|truncate(20) }}
{% endif %} {% endif %}
</a> </a>
<br> <br>

Zobrazit soubor

@ -1,26 +1,26 @@
{% macro staff_nav() %} {% macro staff_nav() %}
<div class="box inline-block pagination"> <div class="box inline-block pagination">
<a href="/staff/account">[Účet]</a>&#32; [<a href="/staff/account">Účet</a>]&#32;
<a href="/staff/accounts">[Účty]</a>&#32; [<a href="/staff/accounts">Účty</a>]&#32;
{% if tcx.perms.owner() || tcx.perms.board_config() || tcx.perms.banners() %} {% if tcx.perms.owner() || tcx.perms.board_config() || tcx.perms.banners() %}
<a href="/staff/boards">[Nástěnky]</a>&#32; [<a href="/staff/boards">Nástěnky</a>]&#32;
{% endif %} {% endif %}
{% if tcx.perms.owner() || tcx.perms.bans() %} {% if tcx.perms.owner() || tcx.perms.bans() %}
<a href="/staff/bans">[Bany]</a>&#32; [<a href="/staff/bans">Bany</a>]&#32;
{% endif %} {% endif %}
{% if tcx.perms.owner() || tcx.perms.banners() %} {% if tcx.perms.owner() || tcx.perms.banners() %}
<a href="/staff/banners">[Bannery]</a>&#32; [<a href="/staff/banners">Bannery</a>]&#32;
{% endif %} {% endif %}
{% if tcx.perms.owner() || tcx.perms.reports() %} {% if tcx.perms.owner() || tcx.perms.reports() %}
<a href="/staff/reports">[Hlášení]</a>&#32; [<a href="/staff/reports">Hlášení</a>]&#32;
{% endif %} {% endif %}
{% if tcx.perms.owner() || tcx.perms.news() %} {% if tcx.perms.owner() || tcx.perms.news() %}
<a href="/staff/news">[Novinky]</a>&#32; [<a href="/staff/news">Novinky</a>]&#32;
{% endif %} {% endif %}
</div> </div>
{% endmacro %} {% endmacro %}

Zobrazit soubor

@ -0,0 +1,13 @@
{% macro static_pagination(base, current) %}
<div class="box inline-block pagination">
{% if current == 1 %}
[Předchozí]
{% else %}
[<a href="{{ base }}?page={{ current - 1 }}">Předchozí</a>]
{% endif %}
&#32;
[<b><a href="{{ base }}?page={{ current }}">{{ current }}</a></b>]&#32;
[<a href="{{ base }}?page={{ current + 1 }}">Další</a>]
</div>
{% endmacro %}

Zobrazit soubor

@ -18,7 +18,7 @@
<form method="post"> <form method="post">
<div class="center"> <div class="center">
{% for thread in threads %} {% for thread in threads %}
{% call catalog_entry::catalog_entry(thread, loop.index, 15) %} {% call catalog_entry::catalog_entry(thread, loop.index, crate::GENERIC_PAGE_SIZE) %}
{% endfor %} {% endfor %}
</div> </div>
<hr> <hr>

Zobrazit soubor

@ -109,6 +109,11 @@
</td> </td>
</tr> </tr>
<tr>
<td class="label">Motiv nástěnky</td>
<td><input name="board_theme" type="text" value="{{ board.config.0.board_theme }}" required=""></td>
</tr>
<tr> <tr>
<td class="label">Vyžadovat obsah ve vlákně</td> <td class="label">Vyžadovat obsah ve vlákně</td>
<td> <td>

Zobrazit soubor

@ -1,13 +1,38 @@
{% import "../macros/pagination.html" as pagination %} {% import "../macros/post-actions.html" as post_actions %}
{% import "../macros/post.html" as post %}
{% import "../macros/staff-nav.html" as staff_nav %} {% import "../macros/staff-nav.html" as staff_nav %}
{% import "../macros/static-pagination.html" as static_pagination %}
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Hlášení{% endblock %} {% block title %}Hlášení{% endblock %}
{% block content %} {% block content %}
<h1 class="title">Hlášení</h1> <div class="center">
<h1 class="title">Hlášení</h1>
<p class="description greentext">&gt;Ukliď to!!!</p>
</div>
{% call staff_nav::staff_nav() %} {% call staff_nav::staff_nav() %}
<hr> <hr>
<h1>Dočasně nedostupné</h1> <form method="post">
{% for post in posts %}
{% call post::post(boards[post.board.as_str()], post, true) %}
<table class="data-table inline-block m-0">
<tr>
<th>IP adresa</th>
<th>Důvod hlášení</th>
</tr>
{% for report in post.reports.0 %}
<tr>
<td><b>{{ report.reporter_ip }}</b> (<img class="icon" src="/static/flags/{{ report.reporter_country }}.png">)</td>
<td>{{ report.reason }}</td>
</tr>
{% endfor %}
</table>
<hr>
{% endfor %}
{% call static_pagination::static_pagination("/staff/reports", page) %}
<hr>
{% call post_actions::post_actions() %}
</form>
{% endblock %} {% endblock %}

Zobrazit soubor

@ -4,6 +4,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block theme %}{{ board.config.0.board_theme }}{% endblock %}
{% block title %}/{{ board.id }}/ - {{ thread.content_nomarkup|inline_post }}{% endblock %} {% block title %}/{{ board.id }}/ - {{ thread.content_nomarkup|inline_post }}{% endblock %}
{% block content %} {% block content %}

1
theme.txt Normální soubor
Zobrazit soubor

@ -0,0 +1 @@
yotsuba.css