Systém hlášení a vizuální změny
Tento commit je obsažen v:
rodič
72fd48d11e
revize
a453130797
217
Cargo.lock
vygenerováno
217
Cargo.lock
vygenerováno
@ -327,6 +327,54 @@ dependencies = [
|
||||
"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]]
|
||||
name = "anyhow"
|
||||
version = "1.0.74"
|
||||
@ -583,7 +631,7 @@ dependencies = [
|
||||
"pure-rust-locales",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-targets",
|
||||
"windows-targets 0.48.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -623,6 +671,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.6"
|
||||
@ -1004,16 +1058,26 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.0"
|
||||
name = "env_filter"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
|
||||
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
|
||||
dependencies = [
|
||||
"humantime",
|
||||
"is-terminal",
|
||||
"log",
|
||||
"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]]
|
||||
@ -1030,7 +1094,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1051,7 +1115,7 @@ checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"home",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1378,7 +1442,7 @@ version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1539,17 +1603,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
@ -1785,7 +1838,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1967,7 +2020,7 @@ dependencies = [
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
"windows-targets 0.48.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2313,7 +2366,7 @@ dependencies = [
|
||||
"libc",
|
||||
"spin 0.9.8",
|
||||
"untrusted",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2363,7 +2416,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2595,7 +2648,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2889,16 +2942,7 @@ dependencies = [
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2990,7 +3034,7 @@ dependencies = [
|
||||
"signal-hook-registry",
|
||||
"socket2 0.5.3",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3167,6 +3211,12 @@ version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
@ -3267,15 +3317,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
@ -3288,7 +3329,7 @@ version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.48.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3297,7 +3338,16 @@ version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
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]]
|
||||
@ -3306,13 +3356,28 @@ version = "0.48.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_gnullvm 0.48.0",
|
||||
"windows_aarch64_msvc 0.48.0",
|
||||
"windows_i686_gnu 0.48.0",
|
||||
"windows_i686_msvc 0.48.0",
|
||||
"windows_x86_64_gnu 0.48.0",
|
||||
"windows_x86_64_gnullvm 0.48.0",
|
||||
"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]]
|
||||
@ -3321,42 +3386,84 @@ version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "winnow"
|
||||
version = "0.5.18"
|
||||
|
@ -15,7 +15,7 @@ chrono-tz = "0.8.5"
|
||||
dotenv = "0.15.0"
|
||||
enumflags2 = "0.7.7"
|
||||
encoding = "0.2.33"
|
||||
env_logger = "0.10.0"
|
||||
env_logger = "0.11.2"
|
||||
glob = "0.3.1"
|
||||
image = "0.24.7"
|
||||
ipnetwork = "0.20.0"
|
||||
|
@ -64,6 +64,7 @@ pub struct BoardCfg {
|
||||
pub flags: bool,
|
||||
pub thread_captcha: String,
|
||||
pub reply_captcha: String,
|
||||
pub board_theme: String,
|
||||
pub require_thread_content: bool,
|
||||
pub require_thread_file: bool,
|
||||
pub require_reply_content: bool,
|
||||
|
@ -5,7 +5,7 @@ use sqlx::{query, query_as, types::Json};
|
||||
use std::net::IpAddr;
|
||||
|
||||
use super::models::{Board, File, Post, Report};
|
||||
use crate::{ctx::Ctx, error::NekrochanError};
|
||||
use crate::{ctx::Ctx, error::NekrochanError, GENERIC_PAGE_SIZE};
|
||||
|
||||
impl Post {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@ -161,8 +161,8 @@ impl Post {
|
||||
LIMIT $1
|
||||
OFFSET $2"#,
|
||||
)
|
||||
.bind(15)
|
||||
.bind((page - 1) * 15)
|
||||
.bind(GENERIC_PAGE_SIZE)
|
||||
.bind((page - 1) * GENERIC_PAGE_SIZE)
|
||||
.fetch_all(ctx.db())
|
||||
.await?;
|
||||
|
||||
@ -181,12 +181,16 @@ impl Post {
|
||||
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(
|
||||
r#"SELECT * FROM overboard
|
||||
WHERE reports != '[]'::jsonb
|
||||
ORDER BY jsonb_array_length(reports), reported DESC"#,
|
||||
WHERE reports != '[]'::jsonb
|
||||
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())
|
||||
.await?;
|
||||
|
||||
@ -195,7 +199,7 @@ impl Post {
|
||||
|
||||
pub async fn read_replies(&self, ctx: &Ctx) -> Result<Vec<Self>, NekrochanError> {
|
||||
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
|
||||
))
|
||||
.bind(self.id)
|
||||
|
10
src/lib.rs
10
src/lib.rs
@ -7,6 +7,8 @@ lazy_static! {
|
||||
RwLock::new(HashMap::new());
|
||||
}
|
||||
|
||||
const GENERIC_PAGE_SIZE: i64 = 15;
|
||||
|
||||
pub mod auth;
|
||||
pub mod cfg;
|
||||
pub mod ctx;
|
||||
@ -22,7 +24,13 @@ pub mod trip;
|
||||
pub mod web;
|
||||
|
||||
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(
|
||||
|
@ -26,7 +26,7 @@ pub async fn edit_posts(
|
||||
|
||||
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
|
||||
.into_iter()
|
||||
.map(|post| (format!("{}/{}", post.board, post.id), post))
|
||||
|
@ -17,7 +17,7 @@ pub struct ActionTemplate {
|
||||
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();
|
||||
|
||||
for id in ids {
|
||||
|
@ -39,7 +39,7 @@ pub async fn report_posts(
|
||||
}
|
||||
|
||||
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 posts_reported = 0;
|
||||
|
@ -2,11 +2,11 @@ use actix_web::{post, web::Data, HttpRequest, HttpResponse};
|
||||
use chrono::{Duration, Utc};
|
||||
use ipnetwork::IpNetwork;
|
||||
use serde::Deserialize;
|
||||
use std::{collections::HashSet, fmt::Write};
|
||||
use std::{collections::HashSet, fmt::Write, net::IpAddr};
|
||||
|
||||
use crate::{
|
||||
ctx::Ctx,
|
||||
db::models::Ban,
|
||||
db::models::{Account, Ban},
|
||||
error::NekrochanError,
|
||||
qsform::QsForm,
|
||||
web::{
|
||||
@ -28,11 +28,13 @@ pub struct StaffPostActionsForm {
|
||||
pub toggle_spoiler: Option<String>,
|
||||
pub toggle_sticky: Option<String>,
|
||||
pub toggle_lock: Option<String>,
|
||||
pub remove_reports: Option<String>,
|
||||
pub ban_user: Option<String>,
|
||||
pub ban_reporters: Option<String>,
|
||||
pub global_ban: Option<String>,
|
||||
pub unappealable_ban: Option<String>,
|
||||
pub ban_reason: Option<String>,
|
||||
pub ban_duration: Option<i64>,
|
||||
pub ban_duration: Option<u64>,
|
||||
pub ban_range: Option<String>,
|
||||
}
|
||||
|
||||
@ -44,7 +46,7 @@ pub async fn staff_post_actions(
|
||||
) -> Result<HttpResponse, NekrochanError> {
|
||||
let tcx = TemplateCtx::new(&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();
|
||||
|
||||
@ -53,6 +55,7 @@ pub async fn staff_post_actions(
|
||||
let mut spoilers_toggled = 0;
|
||||
let mut stickies_toggled = 0;
|
||||
let mut locks_toggled = 0;
|
||||
let mut reports_removed = 0;
|
||||
let mut bans_issued = 0;
|
||||
|
||||
for post in &posts {
|
||||
@ -89,20 +92,30 @@ pub async fn staff_post_actions(
|
||||
|
||||
if form.toggle_sticky.is_some() {
|
||||
post.update_sticky(&ctx).await?;
|
||||
stickies_toggled += post.files.0.len();
|
||||
stickies_toggled += 1;
|
||||
}
|
||||
|
||||
if form.toggle_lock.is_some() {
|
||||
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();
|
||||
|
||||
for post in posts {
|
||||
if let (Some(_), Some(ban_reason), Some(ban_duration), Some(ban_range)) = (
|
||||
form.ban_user.clone(),
|
||||
if let (
|
||||
(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_duration,
|
||||
form.ban_range.clone(),
|
||||
@ -112,63 +125,56 @@ pub async fn staff_post_actions(
|
||||
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;
|
||||
}
|
||||
|
||||
let account = account.username.clone();
|
||||
|
||||
let board = if form.global_ban.is_none() {
|
||||
Some(post.board.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let prefix = if post.ip.is_ipv4() {
|
||||
match ban_range.as_str() {
|
||||
"lan" => 24,
|
||||
"isp" => 16,
|
||||
_ => 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);
|
||||
for ip in &ips_to_ban {
|
||||
ban_ip(
|
||||
&ctx,
|
||||
&account,
|
||||
&form,
|
||||
*ip,
|
||||
post.board.clone(),
|
||||
reason.clone(),
|
||||
duration,
|
||||
&range,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
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 {
|
||||
None
|
||||
} else {
|
||||
Some(Utc::now() + Duration::days(ban_duration))
|
||||
};
|
||||
let content = format!(
|
||||
"{}\n\n<span class=\"jannytext\">(UŽIVATEL BYL ZA TENTO PŘÍSPĚVEK ZABANOVÁN)</span>",
|
||||
post.content
|
||||
);
|
||||
|
||||
Ban::create(&ctx, account, board, ip_range, reason, appealable, expires).await?;
|
||||
post.update_content(&ctx, content, content_nomarkup).await?;
|
||||
}
|
||||
|
||||
let content_nomarkup = format!(
|
||||
"{}\n\n##(UŽIVATEL BYL ZA TENTO PŘÍSPĚVEK ZABANOVÁN)##",
|
||||
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;
|
||||
bans_issued += ips_to_ban.len();
|
||||
already_banned.extend(ips_to_ban);
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,7 +205,7 @@ pub async fn staff_post_actions(
|
||||
if stickies_toggled != 0 {
|
||||
writeln!(
|
||||
&mut response,
|
||||
"[Úspěch] Připnuta/odepnuta vlákna: {stickies_toggled}"
|
||||
"[Úspěch] Připnuty/odepnuty příspěvky: {stickies_toggled}"
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
@ -212,6 +218,14 @@ pub async fn staff_post_actions(
|
||||
.ok();
|
||||
}
|
||||
|
||||
if reports_removed != 0 {
|
||||
writeln!(
|
||||
&mut response,
|
||||
"[Úspěch] Odstraněna hlášení: {reports_removed}"
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
|
||||
if bans_issued != 0 {
|
||||
writeln!(&mut response, "[Úspěch] Uděleny bany: {bans_issued}").ok();
|
||||
}
|
||||
@ -220,3 +234,55 @@ pub async fn staff_post_actions(
|
||||
|
||||
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(())
|
||||
}
|
||||
|
@ -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 mut response = String::new();
|
||||
|
@ -35,7 +35,7 @@ pub async fn edit_posts(
|
||||
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 };
|
||||
|
||||
template_response(&template)
|
||||
|
@ -9,12 +9,7 @@ use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
check_page,
|
||||
ctx::Ctx,
|
||||
db::models::{Board, Post},
|
||||
error::NekrochanError,
|
||||
filters, paginate,
|
||||
web::{tcx::TemplateCtx, template_response},
|
||||
check_page, ctx::Ctx, db::models::{Board, Post}, error::NekrochanError, filters, paginate, web::{tcx::TemplateCtx, template_response}, GENERIC_PAGE_SIZE
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -43,7 +38,7 @@ pub async fn overboard(
|
||||
|
||||
let count = ctx.cache().get("total_threads").await?;
|
||||
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)?;
|
||||
|
||||
|
@ -19,6 +19,11 @@ struct AccountsTemplate {
|
||||
#[get("/staff/accounts")]
|
||||
pub async fn accounts(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> {
|
||||
let tcx = TemplateCtx::new(&ctx, &req).await?;
|
||||
|
||||
if tcx.account.is_none() {
|
||||
return Err(NekrochanError::NotLoggedInError);
|
||||
}
|
||||
|
||||
let accounts = Account::read_all(&ctx).await?;
|
||||
let template = AccountsTemplate { tcx, accounts };
|
||||
|
||||
|
@ -20,6 +20,7 @@ pub struct UpdateBoardConfigForm {
|
||||
flags: Option<String>,
|
||||
thread_captcha: String,
|
||||
reply_captcha: String,
|
||||
board_theme: String,
|
||||
require_thread_content: Option<String>,
|
||||
require_thread_file: Option<String>,
|
||||
require_reply_content: Option<String>,
|
||||
@ -58,6 +59,7 @@ pub async fn update_board_config(
|
||||
let flags = form.flags.is_some();
|
||||
let thread_captcha = form.thread_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_file = form.require_thread_file.is_some();
|
||||
let require_reply_content = form.require_reply_content.is_some();
|
||||
@ -79,6 +81,7 @@ pub async fn update_board_config(
|
||||
flags,
|
||||
thread_captcha,
|
||||
reply_captcha,
|
||||
board_theme,
|
||||
require_thread_content,
|
||||
require_thread_file,
|
||||
require_reply_content,
|
||||
|
@ -5,10 +5,7 @@ use crate::{
|
||||
ctx::Ctx,
|
||||
db::models::Banner,
|
||||
error::NekrochanError,
|
||||
web::{
|
||||
tcx::{account_from_auth, TemplateCtx},
|
||||
template_response,
|
||||
},
|
||||
web::{tcx::TemplateCtx, template_response},
|
||||
};
|
||||
|
||||
#[derive(Template)]
|
||||
@ -21,9 +18,8 @@ struct BannersTemplate {
|
||||
#[get("/staff/banners")]
|
||||
pub async fn banners(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,7 @@ use crate::{
|
||||
db::models::Ban,
|
||||
error::NekrochanError,
|
||||
filters,
|
||||
web::{
|
||||
tcx::{account_from_auth, TemplateCtx},
|
||||
template_response,
|
||||
},
|
||||
web::{tcx::TemplateCtx, template_response},
|
||||
};
|
||||
|
||||
#[derive(Template)]
|
||||
@ -22,9 +19,8 @@ struct BansTemplate {
|
||||
#[get("/staff/bans")]
|
||||
pub async fn bans(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -9,10 +9,7 @@ use crate::{
|
||||
ctx::Ctx,
|
||||
db::models::Board,
|
||||
error::NekrochanError,
|
||||
web::{
|
||||
tcx::{account_from_auth, TemplateCtx},
|
||||
template_response,
|
||||
},
|
||||
web::{tcx::TemplateCtx, template_response},
|
||||
};
|
||||
|
||||
#[derive(Template)]
|
||||
@ -29,9 +26,8 @@ pub async fn board_config(
|
||||
board: Path<String>,
|
||||
) -> Result<HttpResponse, NekrochanError> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,7 @@ use crate::{
|
||||
db::models::Board,
|
||||
error::NekrochanError,
|
||||
filters,
|
||||
web::{
|
||||
tcx::{account_from_auth, TemplateCtx},
|
||||
template_response,
|
||||
},
|
||||
web::{tcx::TemplateCtx, template_response},
|
||||
};
|
||||
|
||||
#[derive(Template)]
|
||||
@ -22,9 +19,8 @@ struct BoardsTemplate {
|
||||
#[get("/staff/boards")]
|
||||
pub async fn boards(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,11 @@ struct NewsTemplate {
|
||||
#[get("/staff/news")]
|
||||
pub async fn news(ctx: Data<Ctx>, req: HttpRequest) -> Result<HttpResponse, NekrochanError> {
|
||||
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 template = NewsTemplate { tcx, news };
|
||||
|
||||
|
@ -9,10 +9,7 @@ use crate::{
|
||||
ctx::Ctx,
|
||||
db::models::Account,
|
||||
error::NekrochanError,
|
||||
web::{
|
||||
tcx::{account_from_auth, TemplateCtx},
|
||||
template_response,
|
||||
},
|
||||
web::{tcx::TemplateCtx, template_response},
|
||||
};
|
||||
|
||||
#[derive(Template)]
|
||||
@ -29,7 +26,10 @@ pub async fn permissions(
|
||||
path: Path<String>,
|
||||
) -> Result<HttpResponse, NekrochanError> {
|
||||
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 = Account::read(&ctx, account.clone())
|
||||
|
@ -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 serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
ctx::Ctx,
|
||||
db::models::{Board, Post},
|
||||
error::NekrochanError,
|
||||
filters,
|
||||
web::{tcx::TemplateCtx, template_response},
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BoardQuery {
|
||||
page: i64,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Template)]
|
||||
#[template(path = "staff/reports.html")]
|
||||
struct ReportsTemplate {
|
||||
tcx: TemplateCtx,
|
||||
boards: HashMap<String, Board>,
|
||||
posts: Vec<Post>,
|
||||
page: i64,
|
||||
}
|
||||
|
||||
#[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 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)
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ summary {
|
||||
|
||||
.edit-box {
|
||||
display: block;
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.form-table input[type="text"],
|
||||
@ -90,7 +90,6 @@ summary {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
|
||||
.reply-mode {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
@ -105,26 +104,25 @@ summary {
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
overflow-x: auto;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
width: 100%;
|
||||
background-color: var(--table-primary);
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.data-table tr:nth-child(2n + 1) {
|
||||
background-color: var(--table-secondary);
|
||||
}
|
||||
|
||||
.data-table th {
|
||||
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 {
|
||||
border: 1px solid var(--table-border);
|
||||
padding: 4px;
|
||||
@ -213,6 +211,10 @@ summary {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.m-0 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.form-table .button,
|
||||
.full-width {
|
||||
display: block;
|
||||
@ -238,8 +240,9 @@ summary {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.board-links {
|
||||
color: var(--board-links-color);
|
||||
.board-links,
|
||||
.pagination {
|
||||
color: var(--link-list-color);
|
||||
}
|
||||
|
||||
.link-separator::after {
|
||||
@ -432,8 +435,8 @@ summary {
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: 1em;
|
||||
vertical-align: text-top;
|
||||
height: 0.8em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.posts-omitted {
|
||||
|
@ -8,7 +8,7 @@
|
||||
--link-hover: #dd0000;
|
||||
--post-link-color: #34345c;
|
||||
--post-link-hover: #dd0000;
|
||||
--board-links-color: #8899aa;
|
||||
--link-list-color: #8899aa;
|
||||
--title-color: #af0a0f;
|
||||
--title-font: tahoma;
|
||||
--hr-color: #d3d3d3;
|
||||
@ -18,8 +18,7 @@
|
||||
/* Tables */
|
||||
--table-head: #9988ee;
|
||||
--table-border: #000000;
|
||||
--table-primary: #ffffff;
|
||||
--table-secondary: #d6daf0;
|
||||
--table-background: #ffffff;
|
||||
/* Forms */
|
||||
--input-color: #ffffff;
|
||||
--input-border: #808080;
|
||||
|
@ -8,7 +8,7 @@
|
||||
--link-hover: #dd0000;
|
||||
--post-link-color: #000080;
|
||||
--post-link-hover: #dd0000;
|
||||
--board-links-color: #bb8866;
|
||||
--link-list-color: #bb8866;
|
||||
--title-color: #af0a0f;
|
||||
--title-font: tahoma;
|
||||
--hr-color: #d9bfb7;
|
||||
@ -18,8 +18,7 @@
|
||||
/* Tables */
|
||||
--table-head: #ffccaa;
|
||||
--table-border: #800000;
|
||||
--table-primary: #ffffff;
|
||||
--table-secondary: #ffffff;
|
||||
--table-background: #ffffff;
|
||||
/* Forms */
|
||||
--input-color: #ffffff;
|
||||
--input-border: #808080;
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h1 class="title">Jsi trans, btw.</h1>
|
||||
<h1 class="title">Zabanován!!!</h1>
|
||||
<table class="data-table">
|
||||
<tr><th>Máš ban!</th></tr>
|
||||
<tr>
|
||||
@ -20,7 +20,7 @@
|
||||
|
||||
 z následujícího důvodu:
|
||||
</b>
|
||||
<div class="post-content">
|
||||
<div class="post-content box">
|
||||
{{ ban.reason }}
|
||||
</div>
|
||||
<span>Udělil <i>{{ ban.issued_by }}</i></span>
|
||||
@ -45,7 +45,7 @@
|
||||
{% else %}
|
||||
<b>Můžeš se pokusit svůj ban odvolat:</b>
|
||||
<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">
|
||||
<tr>
|
||||
<td class="label">Odvolání</td>
|
||||
@ -62,6 +62,6 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</div>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -7,7 +7,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
<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">
|
||||
<!-- Muh flash of unstyled content -->
|
||||
<script>0</script>
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block theme %}{{ board.config.0.board_theme }}{% endblock %}
|
||||
{% block title %}Katalog (/{{ board.id }}/){% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block theme %}{{ board.config.0.board_theme }}{% endblock %}
|
||||
{% block title %}/{{ board.id }}/ - {{ board.name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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">
|
||||
</head>
|
||||
<body>
|
||||
|
@ -1,15 +1,25 @@
|
||||
{% macro pagination(base, pages, current) %}
|
||||
<div class="box inline-block pagination">
|
||||
{% if pages == 0 %}
|
||||
<b><a href="{{ base }}?page=1">[1]</a></b> 
|
||||
{% if current == 1 %}
|
||||
[Předchozí]
|
||||
{% else %}
|
||||
{% for page in 1..(pages + 1) %}
|
||||
{% if page == current %}
|
||||
<b><a href="{{ base }}?page={{ page }}">[{{ page }}]</a></b> 
|
||||
{% else %}
|
||||
<a href="{{ base }}?page={{ page }}">[{{ page }}]</a> 
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
[<a href="{{ base }}?page={{ current - 1 }}">Předchozí</a>]
|
||||
{% endif %}
|
||||
 
|
||||
|
||||
{% for page in 1..(pages + 1) %}
|
||||
{% if page == current %}
|
||||
[<b><a href="{{ base }}?page={{ page }}">{{ page }}</a></b>]
|
||||
{% else %}
|
||||
[<a href="{{ base }}?page={{ page }}">{{ page }}</a>]
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
 
|
||||
|
||||
{% if current == pages %}
|
||||
[Další]
|
||||
{% else %}
|
||||
[<a href="{{ base }}?page={{ current + 1 }}">Další</a>]
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
@ -35,7 +35,6 @@
|
||||
type="text"
|
||||
autocomplete="new-password"
|
||||
value="{{ tcx.password }}"
|
||||
required=""
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
@ -53,7 +52,7 @@
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<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>
|
||||
<td colspan="2">
|
||||
@ -114,6 +113,16 @@
|
||||
</td>
|
||||
</tr>
|
||||
{% 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() %}
|
||||
<tr>
|
||||
<td class="label">Zabanovat uživatele</td>
|
||||
@ -123,6 +132,16 @@
|
||||
</div>
|
||||
</td>
|
||||
</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>
|
||||
<td class="label">Globální ban</td>
|
||||
<td>
|
||||
@ -144,8 +163,8 @@
|
||||
<td><textarea name="ban_reason"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Délka banu (dny)</td>
|
||||
<td><input name="ban_duration" type="number" min="0" placeholder="0 = trvalý ban"></td>
|
||||
<td class="label">Délka banu <span class="small">(dny, 0 = trvalý)</span></td>
|
||||
<td><input name="ban_duration" type="number" min="0" value="0"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Rozsah banu</td>
|
||||
|
@ -44,7 +44,7 @@
|
||||
{% if file.spoiler %}
|
||||
[Spoiler]
|
||||
{% else %}
|
||||
{{ file.original_name }}
|
||||
{{ file.original_name|truncate(20) }}
|
||||
{% endif %}
|
||||
</a>
|
||||
<br>
|
||||
|
@ -1,26 +1,26 @@
|
||||
{% macro staff_nav() %}
|
||||
<div class="box inline-block pagination">
|
||||
<a href="/staff/account">[Účet]</a> 
|
||||
<a href="/staff/accounts">[Účty]</a> 
|
||||
[<a href="/staff/account">Účet</a>] 
|
||||
[<a href="/staff/accounts">Účty</a>] 
|
||||
|
||||
{% if tcx.perms.owner() || tcx.perms.board_config() || tcx.perms.banners() %}
|
||||
<a href="/staff/boards">[Nástěnky]</a> 
|
||||
[<a href="/staff/boards">Nástěnky</a>] 
|
||||
{% endif %}
|
||||
|
||||
{% if tcx.perms.owner() || tcx.perms.bans() %}
|
||||
<a href="/staff/bans">[Bany]</a> 
|
||||
[<a href="/staff/bans">Bany</a>] 
|
||||
{% endif %}
|
||||
|
||||
{% if tcx.perms.owner() || tcx.perms.banners() %}
|
||||
<a href="/staff/banners">[Bannery]</a> 
|
||||
[<a href="/staff/banners">Bannery</a>] 
|
||||
{% endif %}
|
||||
|
||||
{% if tcx.perms.owner() || tcx.perms.reports() %}
|
||||
<a href="/staff/reports">[Hlášení]</a> 
|
||||
[<a href="/staff/reports">Hlášení</a>] 
|
||||
{% endif %}
|
||||
|
||||
{% if tcx.perms.owner() || tcx.perms.news() %}
|
||||
<a href="/staff/news">[Novinky]</a> 
|
||||
[<a href="/staff/news">Novinky</a>] 
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
13
templates/macros/static-pagination.html
Normální soubor
13
templates/macros/static-pagination.html
Normální 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 %}
|
||||
 
|
||||
|
||||
[<b><a href="{{ base }}?page={{ current }}">{{ current }}</a></b>] 
|
||||
[<a href="{{ base }}?page={{ current + 1 }}">Další</a>]
|
||||
</div>
|
||||
{% endmacro %}
|
@ -18,7 +18,7 @@
|
||||
<form method="post">
|
||||
<div class="center">
|
||||
{% 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 %}
|
||||
</div>
|
||||
<hr>
|
||||
|
@ -109,6 +109,11 @@
|
||||
</td>
|
||||
</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>
|
||||
<td class="label">Vyžadovat obsah ve vlákně</td>
|
||||
<td>
|
||||
|
@ -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/static-pagination.html" as static_pagination %}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Hlášení{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="title">Hlášení</h1>
|
||||
<div class="center">
|
||||
<h1 class="title">Hlášení</h1>
|
||||
<p class="description greentext">>Ukliď to!!!</p>
|
||||
</div>
|
||||
{% call staff_nav::staff_nav() %}
|
||||
<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 %}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block theme %}{{ board.config.0.board_theme }}{% endblock %}
|
||||
{% block title %}/{{ board.id }}/ - {{ thread.content_nomarkup|inline_post }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
1
theme.txt
Normální soubor
1
theme.txt
Normální soubor
@ -0,0 +1 @@
|
||||
yotsuba.css
|
Načítá se…
Odkázat v novém úkolu
Zablokovat Uživatele