JS na rozbalování souborů + IP akce
Tento commit je obsažen v:
rodič
a453130797
revize
eecc1a191c
@ -195,6 +195,11 @@ impl Board {
|
|||||||
|
|
||||||
update_overboard(ctx, boards).await?;
|
update_overboard(ctx, boards).await?;
|
||||||
|
|
||||||
|
query("DELETE FROM bans WHERE board = $1")
|
||||||
|
.bind(&self.id)
|
||||||
|
.execute(ctx.db())
|
||||||
|
.await?;
|
||||||
|
|
||||||
query(&format!("DROP TABLE posts_{}", self.id))
|
query(&format!("DROP TABLE posts_{}", self.id))
|
||||||
.execute(ctx.db())
|
.execute(ctx.db())
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -197,6 +197,27 @@ impl Post {
|
|||||||
Ok(posts)
|
Ok(posts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn read_ip_page(
|
||||||
|
ctx: &Ctx,
|
||||||
|
ip: IpAddr,
|
||||||
|
page: i64,
|
||||||
|
) -> Result<Vec<Self>, NekrochanError> {
|
||||||
|
let posts = query_as(
|
||||||
|
r#"SELECT * FROM overboard
|
||||||
|
WHERE ip = $1
|
||||||
|
ORDER BY created DESC
|
||||||
|
LIMIT $2
|
||||||
|
OFFSET $3"#,
|
||||||
|
)
|
||||||
|
.bind(ip)
|
||||||
|
.bind(GENERIC_PAGE_SIZE)
|
||||||
|
.bind((page - 1) * GENERIC_PAGE_SIZE)
|
||||||
|
.fetch_all(ctx.db())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(posts)
|
||||||
|
}
|
||||||
|
|
||||||
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 sticky DESC, created ASC",
|
"SELECT * FROM posts_{} WHERE thread = $1 ORDER BY sticky DESC, created ASC",
|
||||||
|
@ -63,6 +63,7 @@ async fn run() -> Result<(), Error> {
|
|||||||
.service(web::board::board)
|
.service(web::board::board)
|
||||||
.service(web::board_catalog::board_catalog)
|
.service(web::board_catalog::board_catalog)
|
||||||
.service(web::index::index)
|
.service(web::index::index)
|
||||||
|
.service(web::ip_posts::ip_posts)
|
||||||
.service(web::edit_posts::edit_posts)
|
.service(web::edit_posts::edit_posts)
|
||||||
.service(web::login::login_get)
|
.service(web::login::login_get)
|
||||||
.service(web::login::login_post)
|
.service(web::login::login_post)
|
||||||
|
@ -15,6 +15,7 @@ pub enum Permissions {
|
|||||||
BoardConfig,
|
BoardConfig,
|
||||||
News,
|
News,
|
||||||
Jannytext,
|
Jannytext,
|
||||||
|
ViewIPs,
|
||||||
BypassBans,
|
BypassBans,
|
||||||
BypassBoardLock,
|
BypassBoardLock,
|
||||||
BypassThreadLock,
|
BypassThreadLock,
|
||||||
@ -83,6 +84,10 @@ impl PermissionWrapper {
|
|||||||
self.0.contains(Permissions::Jannytext)
|
self.0.contains(Permissions::Jannytext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn view_ips(&self) -> bool {
|
||||||
|
self.0.contains(Permissions::ViewIPs)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bypass_bans(&self) -> bool {
|
pub fn bypass_bans(&self) -> bool {
|
||||||
self.0.contains(Permissions::BypassBans)
|
self.0.contains(Permissions::BypassBans)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use actix_web::{post, web::Data, HttpRequest, HttpResponse};
|
use actix_web::{post, web::Data, HttpRequest, HttpResponse};
|
||||||
use chrono::{Duration, Utc};
|
use chrono::{Duration, Utc};
|
||||||
use ipnetwork::IpNetwork;
|
use ipnetwork::IpNetwork;
|
||||||
|
use redis::AsyncCommands;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{collections::HashSet, fmt::Write, net::IpAddr};
|
use std::{collections::HashSet, fmt::Write, net::IpAddr};
|
||||||
|
|
||||||
@ -26,6 +27,8 @@ pub struct StaffPostActionsForm {
|
|||||||
pub remove_files: Option<String>,
|
pub remove_files: Option<String>,
|
||||||
#[serde(rename = "staff_toggle_spoiler")]
|
#[serde(rename = "staff_toggle_spoiler")]
|
||||||
pub toggle_spoiler: Option<String>,
|
pub toggle_spoiler: Option<String>,
|
||||||
|
pub remove_by_ip_board: Option<String>,
|
||||||
|
pub remove_by_ip_global: 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 remove_reports: Option<String>,
|
||||||
@ -36,6 +39,7 @@ pub struct StaffPostActionsForm {
|
|||||||
pub ban_reason: Option<String>,
|
pub ban_reason: Option<String>,
|
||||||
pub ban_duration: Option<u64>,
|
pub ban_duration: Option<u64>,
|
||||||
pub ban_range: Option<String>,
|
pub ban_range: Option<String>,
|
||||||
|
pub troll_user: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/actions/staff-post-actions")]
|
#[post("/actions/staff-post-actions")]
|
||||||
@ -57,6 +61,7 @@ pub async fn staff_post_actions(
|
|||||||
let mut locks_toggled = 0;
|
let mut locks_toggled = 0;
|
||||||
let mut reports_removed = 0;
|
let mut reports_removed = 0;
|
||||||
let mut bans_issued = 0;
|
let mut bans_issued = 0;
|
||||||
|
let mut users_trolled = 0;
|
||||||
|
|
||||||
for post in &posts {
|
for post in &posts {
|
||||||
if (form.remove_posts.is_some()
|
if (form.remove_posts.is_some()
|
||||||
@ -90,6 +95,30 @@ pub async fn staff_post_actions(
|
|||||||
spoilers_toggled += post.files.0.len();
|
spoilers_toggled += post.files.0.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.remove_by_ip_board.is_some() {
|
||||||
|
let key = format!("by_ip:{}", post.ip);
|
||||||
|
let ip_posts: Vec<String> = ctx.cache().zrange(key, 0, -1).await?;
|
||||||
|
let board_ip_posts = ip_posts
|
||||||
|
.into_iter()
|
||||||
|
.filter(|p| p.starts_with(&format!("{}/", post.board)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for post in get_posts_from_ids(&ctx, &board_ip_posts).await {
|
||||||
|
post.delete(&ctx).await?;
|
||||||
|
posts_removed += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.remove_by_ip_global.is_some() {
|
||||||
|
let key = format!("by_ip:{}", post.ip);
|
||||||
|
let ip_posts: Vec<String> = ctx.cache().zrange(key, 0, -1).await?;
|
||||||
|
|
||||||
|
for post in get_posts_from_ids(&ctx, &ip_posts).await {
|
||||||
|
post.delete(&ctx).await?;
|
||||||
|
posts_removed += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if form.toggle_sticky.is_some() {
|
if form.toggle_sticky.is_some() {
|
||||||
post.update_sticky(&ctx).await?;
|
post.update_sticky(&ctx).await?;
|
||||||
stickies_toggled += 1;
|
stickies_toggled += 1;
|
||||||
@ -99,8 +128,15 @@ pub async fn staff_post_actions(
|
|||||||
post.update_lock(&ctx).await?;
|
post.update_lock(&ctx).await?;
|
||||||
locks_toggled += 1;
|
locks_toggled += 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for post in &posts {
|
||||||
if form.remove_reports.is_some() {
|
if form.remove_reports.is_some() {
|
||||||
|
if !(tcx.perms.owner() || tcx.perms.reports()) {
|
||||||
|
writeln!(&mut response, "[Chyba] Nemáš přístup k hlášením.").ok();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
post.delete_reports(&ctx).await?;
|
post.delete_reports(&ctx).await?;
|
||||||
reports_removed += post.reports.0.len();
|
reports_removed += post.reports.0.len();
|
||||||
}
|
}
|
||||||
@ -108,7 +144,7 @@ pub async fn staff_post_actions(
|
|||||||
|
|
||||||
let mut already_banned = HashSet::new();
|
let mut already_banned = HashSet::new();
|
||||||
|
|
||||||
for post in posts {
|
for post in &posts {
|
||||||
if let (
|
if let (
|
||||||
(Some(_), None) | (None, Some(_)) | (Some(_), Some(_)),
|
(Some(_), None) | (None, Some(_)) | (Some(_), Some(_)),
|
||||||
Some(reason),
|
Some(reason),
|
||||||
@ -132,6 +168,11 @@ pub async fn staff_post_actions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if form.ban_reporters.is_some() {
|
if form.ban_reporters.is_some() {
|
||||||
|
if !(tcx.perms.owner() || tcx.perms.reports()) {
|
||||||
|
writeln!(&mut response, "[Chyba] Nemáš přístup k hlášením.").ok();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ips_to_ban.extend(
|
ips_to_ban.extend(
|
||||||
post.reports
|
post.reports
|
||||||
.0
|
.0
|
||||||
@ -178,6 +219,34 @@ pub async fn staff_post_actions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for post in &posts {
|
||||||
|
if form.troll_user.is_some() {
|
||||||
|
if !(tcx.perms.owner() || tcx.perms.edit_posts()) {
|
||||||
|
writeln!(&mut response, "[Chyba] Nemáš oprávnění upravovat příspěvky.").ok();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(tcx.perms.owner() || tcx.perms.view_ips()) {
|
||||||
|
writeln!(&mut response, "[Chyba] Nemáš oprávnění zobrazovat IP adresy.").ok();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let content_nomarkup = format!(
|
||||||
|
"{}\n\n##({})##",
|
||||||
|
post.content_nomarkup, post.ip
|
||||||
|
);
|
||||||
|
|
||||||
|
let content = format!(
|
||||||
|
"{}\n\n<span class=\"jannytext\">({})</span>",
|
||||||
|
post.content, post.ip
|
||||||
|
);
|
||||||
|
|
||||||
|
post.update_content(&ctx, content, content_nomarkup).await?;
|
||||||
|
users_trolled += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if posts_removed != 0 {
|
if posts_removed != 0 {
|
||||||
writeln!(
|
writeln!(
|
||||||
&mut response,
|
&mut response,
|
||||||
@ -226,6 +295,14 @@ pub async fn staff_post_actions(
|
|||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if users_trolled != 0 {
|
||||||
|
writeln!(
|
||||||
|
&mut response,
|
||||||
|
"[Úspěch] Vytroleni uživatelé: {users_trolled}"
|
||||||
|
)
|
||||||
|
.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();
|
||||||
}
|
}
|
||||||
|
65
src/web/ip_posts.rs
Normální soubor
65
src/web/ip_posts.rs
Normální soubor
@ -0,0 +1,65 @@
|
|||||||
|
use actix_web::{
|
||||||
|
get,
|
||||||
|
web::{Data, Path, Query},
|
||||||
|
HttpRequest, HttpResponse,
|
||||||
|
};
|
||||||
|
use askama::Template;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::{collections::HashMap, net::IpAddr};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ctx::Ctx,
|
||||||
|
db::models::{Board, Post},
|
||||||
|
error::NekrochanError,
|
||||||
|
filters,
|
||||||
|
web::{tcx::TemplateCtx, template_response},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct IpPostsQuery {
|
||||||
|
page: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "ip-posts.html")]
|
||||||
|
struct IpPostsTemplate {
|
||||||
|
tcx: TemplateCtx,
|
||||||
|
ip: IpAddr,
|
||||||
|
boards: HashMap<String, Board>,
|
||||||
|
posts: Vec<Post>,
|
||||||
|
page: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/ip-posts/{ip}")]
|
||||||
|
pub async fn ip_posts(
|
||||||
|
ctx: Data<Ctx>,
|
||||||
|
req: HttpRequest,
|
||||||
|
path: Path<IpAddr>,
|
||||||
|
query: Option<Query<IpPostsQuery>>,
|
||||||
|
) -> Result<HttpResponse, NekrochanError> {
|
||||||
|
let tcx = TemplateCtx::new(&ctx, &req).await?;
|
||||||
|
|
||||||
|
if !(tcx.perms.owner() || tcx.perms.view_ips()) {
|
||||||
|
return Err(NekrochanError::InsufficientPermissionError);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ip = path.into_inner();
|
||||||
|
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_ip_page(&ctx, ip, page).await?;
|
||||||
|
|
||||||
|
let template = IpPostsTemplate {
|
||||||
|
tcx,
|
||||||
|
ip,
|
||||||
|
boards,
|
||||||
|
posts,
|
||||||
|
page,
|
||||||
|
};
|
||||||
|
|
||||||
|
template_response(&template)
|
||||||
|
}
|
@ -14,6 +14,7 @@ pub mod overboard_catalog;
|
|||||||
pub mod staff;
|
pub mod staff;
|
||||||
pub mod tcx;
|
pub mod tcx;
|
||||||
pub mod thread;
|
pub mod thread;
|
||||||
|
pub mod ip_posts;
|
||||||
|
|
||||||
use self::tcx::TemplateCtx;
|
use self::tcx::TemplateCtx;
|
||||||
use crate::{ctx::Ctx, db::models::Ban, error::NekrochanError, filters};
|
use crate::{ctx::Ctx, db::models::Ban, error::NekrochanError, filters};
|
||||||
|
@ -18,9 +18,10 @@ pub struct UpdatePermissionsForm {
|
|||||||
reports: Option<String>,
|
reports: Option<String>,
|
||||||
bans: Option<String>,
|
bans: Option<String>,
|
||||||
banners: Option<String>,
|
banners: Option<String>,
|
||||||
|
board_config: Option<String>,
|
||||||
news: Option<String>,
|
news: Option<String>,
|
||||||
jannytext: Option<String>,
|
jannytext: Option<String>,
|
||||||
board_config: Option<String>,
|
view_ips: Option<String>,
|
||||||
bypass_bans: Option<String>,
|
bypass_bans: Option<String>,
|
||||||
bypass_board_lock: Option<String>,
|
bypass_board_lock: Option<String>,
|
||||||
bypass_thread_lock: Option<String>,
|
bypass_thread_lock: Option<String>,
|
||||||
@ -91,6 +92,10 @@ pub async fn update_permissions(
|
|||||||
permissions |= Permissions::Jannytext;
|
permissions |= Permissions::Jannytext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.view_ips.is_some() {
|
||||||
|
permissions |= Permissions::ViewIPs;
|
||||||
|
}
|
||||||
|
|
||||||
if form.bypass_bans.is_some() {
|
if form.bypass_bans.is_some() {
|
||||||
permissions |= Permissions::BypassBans;
|
permissions |= Permissions::BypassBans;
|
||||||
}
|
}
|
||||||
|
86
static/js/expand-image.js
Normální soubor
86
static/js/expand-image.js
Normální soubor
@ -0,0 +1,86 @@
|
|||||||
|
$(function () {
|
||||||
|
$(".expandable").click(function () {
|
||||||
|
let src_link = $(this).attr("href");
|
||||||
|
|
||||||
|
let is_video = [".mpeg", ".mov", ".mp4", ".webm", ".mkv", ".ogg"].some(
|
||||||
|
(ext) => src_link.endsWith(ext)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!is_video) {
|
||||||
|
toggle_image($(this), src_link);
|
||||||
|
} else {
|
||||||
|
toggle_video($(this), src_link);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(this).toggleClass("expanded");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggle_image(parent, src_link) {
|
||||||
|
let expanded = parent.hasClass("expanded");
|
||||||
|
let thumb = parent.find(".thumb");
|
||||||
|
let src = parent.find(".src");
|
||||||
|
|
||||||
|
if (!expanded) {
|
||||||
|
if (src.length === 0) {
|
||||||
|
thumb.css("opacity", 0.5);
|
||||||
|
parent.append(`<img class="src" src="${src_link}">`);
|
||||||
|
|
||||||
|
let src = parent.find(".src");
|
||||||
|
|
||||||
|
src.hide();
|
||||||
|
|
||||||
|
src.on("load", function () {
|
||||||
|
thumb.hide();
|
||||||
|
thumb.css("opacity", "");
|
||||||
|
src.show();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
thumb.hide();
|
||||||
|
src.show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
src.hide();
|
||||||
|
thumb.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle_video(parent, src_link) {
|
||||||
|
let expanded = parent.hasClass("expanded");
|
||||||
|
let thumb = parent.find(".thumb");
|
||||||
|
let src = parent.parent().find(".src");
|
||||||
|
|
||||||
|
if (!expanded) {
|
||||||
|
if (src.length === 0) {
|
||||||
|
thumb.css("opacity", 0.5);
|
||||||
|
|
||||||
|
parent.append('<span class="closer">[Zavřít]<br></span>')
|
||||||
|
|
||||||
|
parent.parent().append(
|
||||||
|
`<video class="src" src="${src_link}" autoplay="" controls="" loop=""></video>`
|
||||||
|
);
|
||||||
|
|
||||||
|
let src = parent.parent().find(".src");
|
||||||
|
|
||||||
|
src.hide();
|
||||||
|
|
||||||
|
src.on("loadstart", function () {
|
||||||
|
thumb.hide();
|
||||||
|
thumb.css("opacity", "");
|
||||||
|
src.show();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
thumb.hide();
|
||||||
|
src.show();
|
||||||
|
src.get(0).play();
|
||||||
|
parent.find(".closer").show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
src.get(0).pause();
|
||||||
|
src.hide();
|
||||||
|
thumb.show();
|
||||||
|
parent.find(".closer").hide();
|
||||||
|
}
|
||||||
|
}
|
2
static/js/jquery.min.js
vendorováno
Normální soubor
2
static/js/jquery.min.js
vendorováno
Normální soubor
Rozdílový obsah nebyl zobrazen, protože některé řádky jsou příliš dlouhá
404
static/style.css
404
static/style.css
@ -1,62 +1,63 @@
|
|||||||
:root {
|
:root {
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
font-family: var(--font);
|
font-family: var(--font);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--link-color);
|
color: var(--link-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
color: var(--link-hover);
|
color: var(--link-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
details {
|
details {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
details:last-of-type {
|
details:last-of-type {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
img,
|
img,
|
||||||
video {
|
video {
|
||||||
max-height: 90vh;
|
max-width: 100%;
|
||||||
|
max-height: 90vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
border-top: 1px solid var(--hr-color);
|
border-top: 1px solid var(--hr-color);
|
||||||
border-left: none;
|
border-left: none;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
summary {
|
summary {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-table .label {
|
.form-table .label {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
background-color: var(--table-head);
|
background-color: var(--table-head);
|
||||||
border: 1px solid var(--table-border);
|
border: 1px solid var(--table-border);
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-table td {
|
.form-table td {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-box {
|
.edit-box {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-table input[type="text"],
|
.form-table input[type="text"],
|
||||||
@ -66,407 +67,408 @@ summary {
|
|||||||
.form-table select,
|
.form-table select,
|
||||||
.input-wrapper,
|
.input-wrapper,
|
||||||
.edit-box {
|
.edit-box {
|
||||||
-webkit-box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
||||||
-moz-box-sizing: border-box;
|
-moz-box-sizing: border-box;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
background-color: var(--input-color);
|
background-color: var(--input-color);
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border: 1px solid var(--input-border);
|
border: 1px solid var(--input-border);
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-table input[type="checkbox"] {
|
.form-table input[type="checkbox"] {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-table textarea,
|
.form-table textarea,
|
||||||
.edit-box {
|
.edit-box {
|
||||||
height: 8rem;
|
height: 8rem;
|
||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reply-mode {
|
.reply-mode {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: var(--table-head);
|
background-color: var(--table-head);
|
||||||
border: 1px solid var(--table-border);
|
border: 1px solid var(--table-border);
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container > form > .form-table {
|
.container > form > .form-table {
|
||||||
margin: 8px auto;
|
margin: 8px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-wrap {
|
.table-wrap {
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-table {
|
.data-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.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);
|
background-color: var(--table-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-table td:not(.form-table td),
|
.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-table .banner {
|
.data-table .banner {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.news {
|
.news {
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.box {
|
.box {
|
||||||
background-color: var(--box-color);
|
background-color: var(--box-color);
|
||||||
border-right: 1px solid var(--box-border);
|
border-right: 1px solid var(--box-border);
|
||||||
border-bottom: 1px solid var(--box-border);
|
border-bottom: 1px solid var(--box-border);
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.box:target {
|
.box:target {
|
||||||
background-color: var(--hl-box-color);
|
background-color: var(--hl-box-color);
|
||||||
border-right: 1px solid var(--hl-box-border);
|
border-right: 1px solid var(--hl-box-border);
|
||||||
border-bottom: 1px solid var(--hl-box-border);
|
border-bottom: 1px solid var(--hl-box-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
background-color: var(--input-color);
|
background-color: var(--input-color);
|
||||||
border: 1px solid var(--input-border);
|
border: 1px solid var(--input-border);
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.captcha {
|
.captcha {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
image-rendering: pixelated;
|
image-rendering: pixelated;
|
||||||
border: 1px solid var(--input-border);
|
border: 1px solid var(--input-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 720px;
|
max-width: 720px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
color: var(--title-color);
|
color: var(--title-color);
|
||||||
font-family: var(--title-font);
|
font-family: var(--title-font);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
letter-spacing: -2px;
|
letter-spacing: -2px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.big {
|
.big {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small {
|
.small {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.center {
|
.center {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inline-block {
|
.inline-block {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.float-r {
|
.float-r {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fixed-table {
|
.fixed-table {
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.m-0 {
|
.m-0 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-table .button,
|
.form-table .button,
|
||||||
.full-width {
|
.full-width {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.banner {
|
.banner {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
margin: 8px auto;
|
margin: 8px auto;
|
||||||
border: 1px solid var(--box-border);
|
border: 1px solid var(--box-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.headline {
|
.headline {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headline::after {
|
.headline::after {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-links,
|
.board-links,
|
||||||
.pagination {
|
.pagination {
|
||||||
color: var(--link-list-color);
|
color: var(--link-list-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-separator::after {
|
.link-separator::after {
|
||||||
content: " / ";
|
content: " / ";
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-group::before {
|
.link-group::before {
|
||||||
content: " [ ";
|
content: " [ ";
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-group::after {
|
.link-group::after {
|
||||||
content: " ] ";
|
content: " ] ";
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header::after {
|
.header::after {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 8pt;
|
font-size: 8pt;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post {
|
.post {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post.box {
|
.post.box {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
min-width: 400px;
|
min-width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post:last-of-type {
|
.post:last-of-type {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post::after {
|
.post::after {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-links a,
|
.board-links a,
|
||||||
.pagination a,
|
.pagination a,
|
||||||
.post-number {
|
.post-number {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-header input[type="checkbox"] {
|
.post-header input[type="checkbox"] {
|
||||||
margin: 0;
|
vertical-align: middle;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.catalog-entry {
|
.catalog-entry {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 250px;
|
height: 250px;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.catalog-entry .thumb {
|
.catalog-entry .thumb {
|
||||||
display: block;
|
display: block;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 50%;
|
max-height: 50%;
|
||||||
box-shadow: 0 0 3px #000;
|
box-shadow: 0 0 3px #000;
|
||||||
margin: 4px auto;
|
margin: 4px auto;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.catalog-entry .post-content {
|
.catalog-entry .post-content {
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: var(--name-color);
|
color: var(--name-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tripcode {
|
.tripcode {
|
||||||
color: var(--trip-color);
|
color: var(--trip-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.capcode {
|
.capcode {
|
||||||
color: var(--capcode-color);
|
color: var(--capcode-color);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-id {
|
.user-id {
|
||||||
text-shadow: #000 0 0 1px, #000 0 0 1px, #000 0 0 1px, #000 0 0 1px,
|
text-shadow: #000 0 0 1px, #000 0 0 1px, #000 0 0 1px, #000 0 0 1px,
|
||||||
#000 0 0 1px, #000 0 0 1px;
|
#000 0 0 1px, #000 0 0 1px;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
border: 1px solid var(--box-border);
|
border: 1px solid var(--box-border);
|
||||||
padding: 0 2px;
|
padding: 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-files {
|
.post-files {
|
||||||
float: left;
|
float: left;
|
||||||
margin: 0 8px 8px 8px;
|
margin: 0 8px 8px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.multi-files {
|
.multi-files {
|
||||||
float: none;
|
float: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-file {
|
.post-file {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 8pt;
|
font-size: 8pt;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thumb {
|
.thumb {
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-content {
|
.post-content {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post .post-content {
|
.post .post-content {
|
||||||
margin: 1rem 2rem;
|
margin: 1rem 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-content a {
|
.post-content a {
|
||||||
color: var(--post-link-color);
|
color: var(--post-link-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-content a:hover {
|
.post-content a:hover {
|
||||||
color: var(--post-link-hover);
|
color: var(--post-link-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dead-quote {
|
.dead-quote {
|
||||||
color: var(--dead-quote-color);
|
color: var(--dead-quote-color);
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
|
||||||
.greentext {
|
.greentext {
|
||||||
color: var(--greentext-color);
|
color: var(--greentext-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.orangetext {
|
.orangetext {
|
||||||
color: var(--orangetext-color);
|
color: var(--orangetext-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.redtext {
|
.redtext {
|
||||||
color: var(--redtext-color);
|
color: var(--redtext-color);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bluetext {
|
.bluetext {
|
||||||
color: var(--bluetext-color);
|
color: var(--bluetext-color);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.glowtext {
|
.glowtext {
|
||||||
text-shadow: 0 0 40px #00fe20, 0 0 2px #00fe20;
|
text-shadow: 0 0 40px #00fe20, 0 0 2px #00fe20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uh-oh-text {
|
.uh-oh-text {
|
||||||
color: var(--uh-oh-text);
|
color: var(--uh-oh-text);
|
||||||
background-color: var(--uh-oh-color);
|
background-color: var(--uh-oh-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.spoiler {
|
.spoiler {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
background-color: var(--text);
|
background-color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.spoiler:hover {
|
.spoiler:hover {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jannytext {
|
.jannytext {
|
||||||
color: var(--jannytext-color);
|
color: var(--jannytext-color);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
height: 0.8em;
|
height: 0.8em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.posts-omitted {
|
.posts-omitted {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-list {
|
.board-list {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 600px) {
|
@media only screen and (max-width: 600px) {
|
||||||
.thumb {
|
.thumb {
|
||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
max-height: 100px;
|
max-height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post.box {
|
.post.box {
|
||||||
display: block;
|
display: block;
|
||||||
min-width: unset;
|
min-width: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thread > br {
|
.thread > br {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.catalog-entry {
|
.catalog-entry {
|
||||||
width: 140px;
|
width: 140px;
|
||||||
height: 220px;
|
height: 220px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,10 @@
|
|||||||
<meta name="description" content="{{ tcx.cfg.site.description }}">
|
<meta name="description" content="{{ tcx.cfg.site.description }}">
|
||||||
<link rel="stylesheet" href='/static/themes/{% block theme %}{% include "../theme.txt" %}{% endblock %}'>
|
<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 -->
|
<script src="/static/js/jquery.min.js"></script>
|
||||||
<script>0</script>
|
<script src="/static/js/expand-image.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body id="top">
|
||||||
<div class="board-links header">
|
<div class="board-links header">
|
||||||
<span class="link-group"><a href="/">domov</a></span>
|
<span class="link-group"><a href="/">domov</a></span>
|
||||||
{% call board_links::board_links() %}
|
{% call board_links::board_links() %}
|
||||||
@ -35,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
<div class="footer">
|
<div id="bottom" class="footer">
|
||||||
<div class="box inline-block">
|
<div class="box inline-block">
|
||||||
<a href="https://git.nekrofilie.com/sneedmaster/nekrochan">nekrochan</a> - Projekt <a href="https://nekrofilie.com/">Nekrofilie</a>
|
<a href="https://git.nekrofilie.com/sneedmaster/nekrochan">nekrochan</a> - Projekt <a href="https://nekrofilie.com/">Nekrofilie</a>
|
||||||
<br>
|
<br>
|
||||||
|
@ -21,7 +21,9 @@
|
|||||||
<div class="box">
|
<div class="box">
|
||||||
<h2 class="headline">
|
<h2 class="headline">
|
||||||
<span>{{ news.title }}</span>
|
<span>{{ news.title }}</span>
|
||||||
<span class="float-r">{{ news.author }} - {{ news.created|czech_datetime }}</span>
|
<span class="float-r">
|
||||||
|
{{ news.author }} - <span title="{{ news.created|czech_humantime }}">{{ news.created|czech_datetime }}</span>
|
||||||
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="post-content">{{ news.content|safe }}</div>
|
<div class="post-content">{{ news.content|safe }}</div>
|
||||||
|
26
templates/ip-posts.html
Normální soubor
26
templates/ip-posts.html
Normální soubor
@ -0,0 +1,26 @@
|
|||||||
|
{% import "./macros/post-actions.html" as post_actions %}
|
||||||
|
{% import "./macros/post.html" as post %}
|
||||||
|
{% import "./macros/static-pagination.html" as static_pagination %}
|
||||||
|
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Příspěvky od [{{ ip }}]{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1 class="title">Příspěvky od [{{ ip }}]</h1>
|
||||||
|
<hr>
|
||||||
|
<form method="post">
|
||||||
|
<div class="thread">
|
||||||
|
{% for post in posts %}
|
||||||
|
<b>Příspěvek z <a href="/boards/{{ post.board }}">/{{ post.board }}/</a></b>
|
||||||
|
<br>
|
||||||
|
{% call post::post(boards[post.board.as_str()], post, true) %}
|
||||||
|
<br>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
{% call static_pagination::static_pagination("/ip-posts/{}"|format(ip), page) %}
|
||||||
|
<hr>
|
||||||
|
{% call post_actions::post_actions() %}
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
@ -67,7 +67,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</details>
|
</details>
|
||||||
<br>
|
<br>
|
||||||
{% if tcx.perms.owner() || tcx.perms.manage_posts() || tcx.perms.bans() || tcx.perms.edit_posts() %}
|
{% if tcx.perms.owner() || tcx.perms.edit_posts() || tcx.perms.manage_posts() || tcx.perms.reports() || tcx.perms.bans() %}
|
||||||
<details>
|
<details>
|
||||||
<summary class="box">Uklízečské akce</summary>
|
<summary class="box">Uklízečské akce</summary>
|
||||||
<table class="form-table">
|
<table class="form-table">
|
||||||
@ -96,6 +96,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label">Odstranit od IP na nástěnce</td>
|
||||||
|
<td>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<input name="remove_by_ip_board" type="checkbox">
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label">Odstranit od IP globálně</td>
|
||||||
|
<td>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<input name="remove_by_ip_global" type="checkbox">
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label">Připnout/odepnout</td>
|
<td class="label">Připnout/odepnout</td>
|
||||||
<td>
|
<td>
|
||||||
@ -177,6 +193,14 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if tcx.perms.owner() || (tcx.perms.edit_posts() && tcx.perms.view_ips()) %}
|
||||||
|
<td class="label">Vytrolit uživatele</td>
|
||||||
|
<td>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<input name="troll_user" type="checkbox">
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<input
|
<input
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
<div id="{{ post.id }}" class="post{% if boxed %} box{% endif %}">
|
<div id="{{ post.id }}" class="post{% if boxed %} box{% endif %}">
|
||||||
<div class="post-header">
|
<div class="post-header">
|
||||||
<input name="posts[]" type="checkbox" value="{{ post.board }}/{{ post.id }}"> 
|
<input name="posts[]" type="checkbox" value="{{ post.board }}/{{ post.id }}"> 
|
||||||
|
{% if tcx.perms.owner() || tcx.perms.view_ips() %}
|
||||||
|
<b><a title="Více příspěvků od uživatele [{{ post.ip }}]" href="/ip-posts/{{ post.ip }}">[+]</a></b> 
|
||||||
|
{% endif %}
|
||||||
{% if let Some(email) = post.email %}
|
{% if let Some(email) = post.email %}
|
||||||
<a class="name" rel="nofollow" href="mailto:{{ email }}">{{ post.name }}</a> 
|
<a class="name" rel="nofollow" href="mailto:{{ email }}">{{ post.name }}</a> 
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -11,14 +14,14 @@
|
|||||||
<span class="tripcode">{{ tripcode }}</span> 
|
<span class="tripcode">{{ tripcode }}</span> 
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if let Some(capcode) = post.capcode %}
|
{% if let Some(capcode) = post.capcode %}
|
||||||
<span class="capcode">## {{ capcode }} <img class="icon" src="/favicon.ico"></span> 
|
<span title="Tento uživatel to dělá ZDARMA!" class="capcode">## {{ capcode }} <img class="icon" src="/favicon.ico"></span> 
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if tcx.ip == post.ip %}
|
{% if tcx.ip == post.ip %}
|
||||||
{# Technically not a tripcode or something but same styling #}
|
{# Technically not a tripcode or something but same styling #}
|
||||||
<i class="tripcode">(Ty)</i> 
|
<i class="tripcode">(Ty)</i> 
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if board.config.0.flags %}
|
{% if board.config.0.flags %}
|
||||||
<img class="icon" src="/static/flags/{{ post.country }}.png"> 
|
<img title="Země: {{ post.country }}" class="icon" src="/static/flags/{{ post.country }}.png"> 
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span title="{{ post.created|czech_datetime }}">{{ post.created|czech_humantime }}</span> 
|
<span title="{{ post.created|czech_datetime }}">{{ post.created|czech_humantime }}</span> 
|
||||||
{% if board.config.0.user_ids %}
|
{% if board.config.0.user_ids %}
|
||||||
@ -26,10 +29,10 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="post-number" href="{{ post.post_url() }}">Č.{{ post.id }}</a> 
|
<a class="post-number" href="{{ post.post_url() }}">Č.{{ post.id }}</a> 
|
||||||
{% if post.sticky %}
|
{% if post.sticky %}
|
||||||
<img class="icon" src="/static/icons/sticky.png"> 
|
<img title="Připnuto" class="icon" src="/static/icons/sticky.png"> 
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if post.locked %}
|
{% if post.locked %}
|
||||||
<img class="icon" src="/static/icons/locked.png"> 
|
<img title="Uzamčeno" class="icon" src="/static/icons/locked.png"> 
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if !boxed %}
|
{% if !boxed %}
|
||||||
@ -40,7 +43,7 @@
|
|||||||
<div class="post-files{% if post.files.0.len() > 1 %} multi-files{% endif %}">
|
<div class="post-files{% if post.files.0.len() > 1 %} multi-files{% endif %}">
|
||||||
{% for file in post.files.0 %}
|
{% for file in post.files.0 %}
|
||||||
<div class="post-file">
|
<div class="post-file">
|
||||||
<a download="{{ file.original_name }}" href="{{ file.file_url() }}">
|
<a title="Stáhnout ({{ file.original_name }})" download="{{ file.original_name }}" href="{{ file.file_url() }}">
|
||||||
{% if file.spoiler %}
|
{% if file.spoiler %}
|
||||||
[Spoiler]
|
[Spoiler]
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -50,7 +53,7 @@
|
|||||||
<br>
|
<br>
|
||||||
({{ file.size|filesizeformat }}, {{ file.width }}x{{ file.height }})
|
({{ file.size|filesizeformat }}, {{ file.width }}x{{ file.height }})
|
||||||
<br>
|
<br>
|
||||||
<a target="_blank" href="{{ file.file_url() }}">
|
<a class="expandable" target="_blank" href="{{ file.file_url() }}">
|
||||||
<img class="thumb" src="{{ file.thumb_url() }}">
|
<img class="thumb" src="{{ file.thumb_url() }}">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,7 +9,9 @@
|
|||||||
<div class="news box">
|
<div class="news box">
|
||||||
<h2 class="headline">
|
<h2 class="headline">
|
||||||
<span>{{ newspost.title }}</span>
|
<span>{{ newspost.title }}</span>
|
||||||
<span class="float-r">{{ newspost.author }} - {{ newspost.created|czech_datetime }}</span>
|
<span class="float-r">
|
||||||
|
{{ newspost.author }} - <span title="{{ newspost.created|czech_humantime }}">{{ newspost.created|czech_datetime }}</span>
|
||||||
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="post-content">{{ newspost.content|safe }}</div>
|
<div class="post-content">{{ newspost.content|safe }}</div>
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<td><input name="accounts[]" type="checkbox" value="{{ account.username }}" {% if !tcx.perms.owner() %}disabled=""{% endif %}></td>
|
<td><input name="accounts[]" type="checkbox" value="{{ account.username }}" {% if !tcx.perms.owner() %}disabled=""{% endif %}></td>
|
||||||
<td>{{ account.username }}</td>
|
<td>{{ account.username }}</td>
|
||||||
<td>{% if account.owner %}Ano{% else %}Ne{% endif %}</td>
|
<td>{% if account.owner %}Ano{% else %}Ne{% endif %}</td>
|
||||||
<td>{{ account.created|czech_datetime }}</td>
|
<td title="{{ account.created|czech_humantime }}">{{ account.created|czech_datetime }}</td>
|
||||||
<td>{{ account.permissions.0 }} <a href="/staff/permissions/{{ account.username }}">[Zobrazit]</a></td>
|
<td>{{ account.permissions.0 }} <a href="/staff/permissions/{{ account.username }}">[Zobrazit]</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -38,8 +38,12 @@
|
|||||||
<td>{{ ban.issued_by }}</td>
|
<td>{{ ban.issued_by }}</td>
|
||||||
<td>{% if ban.appealable %}Ano{% else %}Ne{% endif %}</td>
|
<td>{% if ban.appealable %}Ano{% else %}Ne{% endif %}</td>
|
||||||
<td>{% if let Some(appeal) = ban.appeal %}<div class="post-content">{{ appeal }}</div>{% else %}-{% endif %}</td>
|
<td>{% if let Some(appeal) = ban.appeal %}<div class="post-content">{{ appeal }}</div>{% else %}-{% endif %}</td>
|
||||||
<td>{{ ban.created|czech_datetime }}</td>
|
<td title="ban.{{ ban.created|czech_humantime }}">{{ ban.created|czech_datetime }}</td>
|
||||||
<td>{% if let Some(expires) = ban.expires %}<span title="{{ expires|czech_datetime }}">{{ expires|czech_humantime }}</span>{% else %}<i>Nikdy</i>{% endif %}</td>
|
{% if let Some(expires) = ban.expires %}
|
||||||
|
<td title="{{ expires|czech_humantime }}">{{ expires|czech_datetime }}</td>
|
||||||
|
{% else %}
|
||||||
|
<td>Nikdy</td>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<td>/{{ board.id }}/</td>
|
<td>/{{ board.id }}/</td>
|
||||||
<td>{{ board.name }}</td>
|
<td>{{ board.name }}</td>
|
||||||
<td>{{ board.description }}</td>
|
<td>{{ board.description }}</td>
|
||||||
<td>{{ board.created|czech_datetime }}</td>
|
<td title="{{ board.created|czech_humantime }}">{{ board.created|czech_datetime }}</td>
|
||||||
<td>{% if tcx.perms.owner() || tcx.perms.board_config() %}<a href="/staff/board-config/{{ board.id }}">[Zobrazit]</a>{% else %}-{% endif %}</td>
|
<td>{% if tcx.perms.owner() || tcx.perms.board_config() %}<a href="/staff/board-config/{{ board.id }}">[Zobrazit]</a>{% else %}-{% endif %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -11,7 +11,9 @@
|
|||||||
<div class="box">
|
<div class="box">
|
||||||
<h2 class="headline">
|
<h2 class="headline">
|
||||||
<span>{{ newspost.title }}</span>
|
<span>{{ newspost.title }}</span>
|
||||||
<span class="float-r">{{ newspost.author }} - {{ newspost.created|czech_datetime }}</span>
|
<span class="float-r">
|
||||||
|
{{ newspost.author }} - <span title="{{ newspost.created|czech_humantime }}">{{ newspost.created|czech_datetime }}</span>
|
||||||
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
<hr>
|
<hr>
|
||||||
<textarea class="edit-box" name="{{ newspost.id }}">{{ newspost.content_nomarkup }}</textarea>
|
<textarea class="edit-box" name="{{ newspost.id }}">{{ newspost.content_nomarkup }}</textarea>
|
||||||
|
@ -115,6 +115,15 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="label">Zobrazit IP adresy</td>
|
||||||
|
<td>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<input name="view_ips" type="checkbox"{% if account.perms().view_ips() %} checked="checked"{% endif %}{% if !tcx.perms.owner() %} disabled=""{% endif %}>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label">Obejít ban</td>
|
<td class="label">Obejít ban</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% for post in posts %}
|
{% for post in posts %}
|
||||||
|
<b>Příspěvek z <a href="/boards/{{ post.board }}">/{{ post.board }}/</a></b>
|
||||||
|
<br>
|
||||||
{% call post::post(boards[post.board.as_str()], post, true) %}
|
{% call post::post(boards[post.board.as_str()], post, true) %}
|
||||||
<table class="data-table inline-block m-0">
|
<table class="data-table inline-block m-0">
|
||||||
<tr>
|
<tr>
|
||||||
|
Načítá se…
Odkázat v novém úkolu
Zablokovat Uživatele