+89 -4 +/-9 browse
1 | diff --git a/Cargo.lock b/Cargo.lock |
2 | index 3fcba1a..95f65b9 100644 |
3 | --- a/Cargo.lock |
4 | +++ b/Cargo.lock |
5 | @@ -2036,6 +2036,7 @@ dependencies = [ |
6 | "serde", |
7 | "serde_json", |
8 | "serde_urlencoded", |
9 | + "stderrlog", |
10 | "tempfile", |
11 | "tokio", |
12 | "tower", |
13 | diff --git a/mailpot-tests/src/lib.rs b/mailpot-tests/src/lib.rs |
14 | index dccb32b..d759938 100644 |
15 | --- a/mailpot-tests/src/lib.rs |
16 | +++ b/mailpot-tests/src/lib.rs |
17 | @@ -42,7 +42,7 @@ pub fn init_stderr_logging() { |
18 | INIT_STDERR_LOGGING.call_once(|| { |
19 | stderrlog::new() |
20 | .quiet(false) |
21 | - .verbosity(15) |
22 | + .verbosity(log::LevelFilter::Trace) |
23 | .show_module_names(true) |
24 | .timestamp(stderrlog::Timestamp::Millisecond) |
25 | .init() |
26 | diff --git a/rest-http/src/main.rs b/rest-http/src/main.rs |
27 | index 180de46..704e406 100644 |
28 | --- a/rest-http/src/main.rs |
29 | +++ b/rest-http/src/main.rs |
30 | @@ -7,9 +7,13 @@ async fn main() { |
31 | let config_path = std::env::args() |
32 | .nth(1) |
33 | .expect("Expected configuration file path as first argument."); |
34 | + #[cfg(test)] |
35 | + let verbosity = log::LevelFilter::Trace; |
36 | + #[cfg(not(test))] |
37 | + let verbosity = log::LevelFilter::Info; |
38 | stderrlog::new() |
39 | .quiet(false) |
40 | - .verbosity(15) |
41 | + .verbosity(verbosity) |
42 | .show_module_names(true) |
43 | .timestamp(stderrlog::Timestamp::Millisecond) |
44 | .init() |
45 | diff --git a/web/Cargo.toml b/web/Cargo.toml |
46 | index a203fc7..3e0bef5 100644 |
47 | --- a/web/Cargo.toml |
48 | +++ b/web/Cargo.toml |
49 | @@ -34,6 +34,7 @@ percent-encoding = { version = "^2.1" } |
50 | rand = { version = "^0.8", features = ["min_const_gen"] } |
51 | serde = { version = "^1", features = ["derive", ] } |
52 | serde_json = "^1" |
53 | + stderrlog = "^0.5" |
54 | tempfile = { version = "^3.5" } |
55 | tokio = { version = "1", features = ["full"] } |
56 | tower-http = { version = "^0.3" } |
57 | diff --git a/web/src/main.rs b/web/src/main.rs |
58 | index 231ac2c..df233ad 100644 |
59 | --- a/web/src/main.rs |
60 | +++ b/web/src/main.rs |
61 | @@ -20,7 +20,7 @@ |
62 | use std::{collections::HashMap, sync::Arc}; |
63 | |
64 | use chrono::TimeZone; |
65 | - use mailpot::{Configuration, Connection}; |
66 | + use mailpot::{log, Configuration, Connection}; |
67 | use mailpot_web::*; |
68 | use minijinja::value::Value; |
69 | use rand::Rng; |
70 | @@ -156,6 +156,17 @@ async fn main() { |
71 | |
72 | return; |
73 | } |
74 | + #[cfg(test)] |
75 | + let verbosity = log::LevelFilter::Trace; |
76 | + #[cfg(not(test))] |
77 | + let verbosity = log::LevelFilter::Info; |
78 | + stderrlog::new() |
79 | + .quiet(false) |
80 | + .verbosity(verbosity) |
81 | + .show_module_names(true) |
82 | + .timestamp(stderrlog::Timestamp::Millisecond) |
83 | + .init() |
84 | + .unwrap(); |
85 | let conf = Configuration::from_file(config_path).unwrap(); |
86 | let app = create_app(new_state(conf)); |
87 | |
88 | diff --git a/web/src/minijinja_utils.rs b/web/src/minijinja_utils.rs |
89 | index b7b0c04..ce77da1 100644 |
90 | --- a/web/src/minijinja_utils.rs |
91 | +++ b/web/src/minijinja_utils.rs |
92 | @@ -19,6 +19,8 @@ |
93 | |
94 | //! Utils for templates with the [`minijinja`] crate. |
95 | |
96 | + use std::fmt::Write; |
97 | + |
98 | use super::*; |
99 | |
100 | mod compressed; |
101 | @@ -150,6 +152,20 @@ impl Object for MailingList { |
102 | "unsubscription_mailto" => Ok(Value::from_serializable( |
103 | &self.inner.unsubscription_mailto(), |
104 | )), |
105 | + "topics" => { |
106 | + let mut ul = String::new(); |
107 | + write!(&mut ul, r#"<ul class="tags inline">"#)?; |
108 | + for topic in &self.topics { |
109 | + write!( |
110 | + &mut ul, |
111 | + r#"<li class="tag" style="--red:110;--green:120;--blue:180;"><span class="tag-name">"# |
112 | + )?; |
113 | + write!(&mut ul, "{}", topic)?; |
114 | + write!(&mut ul, r#"</span></li>"#)?; |
115 | + } |
116 | + write!(&mut ul, r#"</ul>"#)?; |
117 | + Ok(Value::from_safe_string(ul)) |
118 | + } |
119 | _ => Err(Error::new( |
120 | minijinja::ErrorKind::UnknownMethod, |
121 | format!("object has no method named {name}"), |
122 | diff --git a/web/src/templates/css.html b/web/src/templates/css.html |
123 | index c389ad1..2029514 100644 |
124 | --- a/web/src/templates/css.html |
125 | +++ b/web/src/templates/css.html |
126 | @@ -161,6 +161,7 @@ |
127 | --code-foreground: #124; |
128 | --code-background: #8fbcbb; |
129 | --a-visited-text: var(--a-normal-text); |
130 | + --tag-border-color: black; |
131 | } |
132 | |
133 | @media (prefers-color-scheme: light) { |
134 | @@ -237,6 +238,7 @@ |
135 | --a-hover-bg: #bfbfbf40; |
136 | --a-active-text: #c00; |
137 | --a-active-underline: #c00; |
138 | + --tag-border-color: #0000005e; |
139 | color-scheme: light; |
140 | } |
141 | } |
142 | @@ -316,6 +318,7 @@ |
143 | --a-hover-bg: #bfbfbf40; |
144 | --a-active-text: #c00; |
145 | --a-active-underline: #c00; |
146 | + --tag-border-color: #000; |
147 | |
148 | color-scheme: dark; |
149 | } |
150 | @@ -995,4 +998,49 @@ |
151 | height:1px; |
152 | overflow:hidden; |
153 | } |
154 | + |
155 | + < |
156 | + ul.tags.inline { |
157 | + display: contents; |
158 | + } |
159 | + |
160 | + ul.tags { |
161 | + list-style: none; |
162 | + margin: 0; |
163 | + padding: 0; |
164 | + height: max-content; |
165 | + vertical-align: baseline; |
166 | + display: inline-flex; |
167 | + gap: 0.8ex; |
168 | + flex-flow: row wrap; |
169 | + } |
170 | + |
171 | + .tag { |
172 | + --padding-top-bottom: 1px; |
173 | + --padding-left-right: 5.4px; |
174 | + |
175 | + display: inline-block; |
176 | + border: 1px solid var(--tag-border-color); |
177 | + border-radius:.2rem; |
178 | + color: #555; |
179 | + font-size: .9rem; |
180 | + padding: 0px 0.4em 1px 0.4em; |
181 | + padding: var(--padding-top-bottom) var(--padding-left-right); |
182 | + text-decoration: none; |
183 | + |
184 | + --aa-brightness: ((var(--red) * 299) + (var(--green) * 587) + (var(--blue) * 114)) / 1000; |
185 | + --aa-color: calc((var(--aa-brightness) - 128) * -1000); |
186 | + background: rgb(var(--red), var(--green), var(--blue)); |
187 | + color: rgb(var(--aa-color), var(--aa-color), var(--aa-color)); |
188 | + min-width: max-content; |
189 | + max-height: calc(1.5cap + var(--padding-top-bottom)); |
190 | + min-height: calc(1.5cap + var(--padding-top-bottom)); |
191 | + } |
192 | + |
193 | + |
194 | + |
195 | + span.tag-name a { |
196 | + text-decoration: none; |
197 | + color: inherit; |
198 | + } |
199 | </style> |
200 | diff --git a/web/src/templates/lists.html b/web/src/templates/lists.html |
201 | index b9582bc..e1f8c45 100644 |
202 | --- a/web/src/templates/lists.html |
203 | +++ b/web/src/templates/lists.html |
204 | @@ -5,7 +5,7 @@ |
205 | <dl class="lists" aria-label="list of mailing lists"> |
206 | {% for l in lists %} |
207 | <dt aria-label="mailing list name"><a href="{{ list_path(l.list.id) }}">{{ l.list.name }}</a></dt> |
208 | - <dd><span aria-label="mailing list description"{% if not l.list.description %} class="no-description"{% endif %}>{{ l.list.description if l.list.description else "no description" }}</span> | {{ l.posts|length }} post{{ l.posts|length|pluralize("","s") }}{% if l.newest %} | <time datetime="{{ l.newest }}">{{ l.newest }}</time>{% endif %}</dd> |
209 | + <dd><span aria-label="mailing list description"{% if not l.list.description %} class="no-description"{% endif %}>{{ l.list.description if l.list.description else "no description" }}</span> | {{ l.posts|length }} post{{ l.posts|length|pluralize("","s") }}{% if l.newest %} | <time datetime="{{ l.newest }}">{{ l.newest }}</time>{% endif %}{% if l.list.topics|length > 0 %}<br aria-hidden="true"><br aria-hidden="true"><span><em>Topics</em>:</span> {{ l.list.topics() }}{% endif %}</dd> |
210 | {% endfor %} |
211 | </dl> |
212 | </div> |
213 | diff --git a/web/src/templates/lists/list.html b/web/src/templates/lists/list.html |
214 | index dcd335d..4844855 100644 |
215 | --- a/web/src/templates/lists/list.html |
216 | +++ b/web/src/templates/lists/list.html |
217 | @@ -1,5 +1,9 @@ |
218 | {% include "header.html" %} |
219 | <div class="body"> |
220 | + {% if list.topics|length > 0 %}<span><em>Topics</em>:</span> {{ list.topics() }} |
221 | + <br aria-hidden="true"> |
222 | + <br aria-hidden="true"> |
223 | + {% endif %} |
224 | {% if list.description %} |
225 | <p title="mailing list description">List description: {{ list.description }}</p> |
226 | {% else %} |