Author:
Hash:
Timestamp:
+77 -145 +/-15 browse
Kevin Schoon [me@kevinschoon.com]
335ba580261377adcc81fda44d3a9fb79f9ae670
Thu, 04 Jan 2024 11:57:58 +0000 (2.0 years ago)
| 1 | diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md |
| 2 | index c27faf8..0180dbb 100644 |
| 3 | --- a/ATTRIBUTIONS.md |
| 4 | +++ b/ATTRIBUTIONS.md |
| 5 | @@ -3,6 +3,8 @@ |
| 6 | Ayllu would not be possible without many free software projects. |
| 7 | |
| 8 | [Git](https://git-scm.com/) |
| 9 | + [Meli](https://meli-email.org/) |
| 10 | + [Mailpot](https://git.meli-email.org/meli/mailpot) |
| 11 | |
| 12 | ## Many [Rust](https://www.rust-lang.org/) Libraries |
| 13 | |
| 14 | diff --git a/src/config.rs b/src/config.rs |
| 15 | index 9cdd349..0895b9d 100644 |
| 16 | --- a/src/config.rs |
| 17 | +++ b/src/config.rs |
| 18 | @@ -175,11 +175,21 @@ impl Xmpp { |
| 19 | } |
| 20 | |
| 21 | #[derive(Deserialize, Serialize, Clone, Debug, Default)] |
| 22 | + pub struct SubscriptionPolicy { |
| 23 | + pub send_confirmation: bool, |
| 24 | + pub kind: String, |
| 25 | + } |
| 26 | + |
| 27 | + #[derive(Deserialize, Serialize, Clone, Debug, Default)] |
| 28 | pub struct MailingList { |
| 29 | pub id: String, |
| 30 | pub name: Option<String>, |
| 31 | pub address: String, |
| 32 | + pub request_address: String, |
| 33 | pub description: String, |
| 34 | + pub topics: Vec<String>, |
| 35 | + pub post_policy: String, |
| 36 | + pub subscription_policy: SubscriptionPolicy, |
| 37 | } |
| 38 | |
| 39 | #[derive(Deserialize, Serialize, Clone, Debug, Default)] |
| 40 | @@ -280,8 +290,7 @@ impl Configurable for Config { |
| 41 | Err(e) => { |
| 42 | return Err(format!( |
| 43 | "failed to load themes from path: {} ({})", |
| 44 | - self.web.themes_path, |
| 45 | - e |
| 46 | + self.web.themes_path, e |
| 47 | ) |
| 48 | .into()) |
| 49 | } |
| 50 | @@ -378,9 +387,4 @@ Disallow: /*/*/chart/* |
| 51 | // opts.render.github_pre_lang = false; |
| 52 | opts |
| 53 | } |
| 54 | - |
| 55 | - // returns if any discussion plugins are enabled |
| 56 | - pub fn discuss_enabled(&self) -> bool { |
| 57 | - self.mail.is_some() || self.xmpp.is_some() |
| 58 | - } |
| 59 | } |
| 60 | diff --git a/src/web2/routes/about.rs b/src/web2/routes/about.rs |
| 61 | index cb9d015..71f1fd2 100644 |
| 62 | --- a/src/web2/routes/about.rs |
| 63 | +++ b/src/web2/routes/about.rs |
| 64 | @@ -16,7 +16,7 @@ pub async fn serve( |
| 65 | ctx.insert("title", "about"); |
| 66 | ctx.insert( |
| 67 | "nav_elements", |
| 68 | - &navigation::global("about", cfg.discuss_enabled()), |
| 69 | + &navigation::global("about", cfg.mail.is_some()), |
| 70 | ); |
| 71 | let options = ComrakOptions::default(); |
| 72 | let mut plugins = ComrakPlugins::default(); |
| 73 | diff --git a/src/web2/routes/config.rs b/src/web2/routes/config.rs |
| 74 | index 0b6f19b..859122a 100644 |
| 75 | --- a/src/web2/routes/config.rs |
| 76 | +++ b/src/web2/routes/config.rs |
| 77 | @@ -22,7 +22,7 @@ pub async fn serve( |
| 78 | ctx.insert("themes", &cfg.web.themes); |
| 79 | ctx.insert( |
| 80 | "nav_elements", |
| 81 | - &navigation::global("config", cfg.discuss_enabled()), |
| 82 | + &navigation::global("config", cfg.mail.is_some()), |
| 83 | ); |
| 84 | let body = templates.render("config.html", &ctx)?; |
| 85 | Ok(Html(body)) |
| 86 | diff --git a/src/web2/routes/discuss.rs b/src/web2/routes/discuss.rs |
| 87 | deleted file mode 100644 |
| 88 | index c723d3b..0000000 |
| 89 | --- a/src/web2/routes/discuss.rs |
| 90 | +++ /dev/null |
| 91 | @@ -1,32 +0,0 @@ |
| 92 | - use axum::{extract::Extension, response::Html}; |
| 93 | - |
| 94 | - use crate::config::Config; |
| 95 | - use crate::web2::error::Error; |
| 96 | - use crate::web2::middleware::rpc_initiator::{Initiator, Kind as InitiatorKind}; |
| 97 | - use crate::web2::middleware::template::Template; |
| 98 | - use crate::web2::util; |
| 99 | - use crate::web2::util::navigation; |
| 100 | - |
| 101 | - pub async fn serve( |
| 102 | - Extension(cfg): Extension<Config>, |
| 103 | - Extension(initiator): Extension<Initiator>, |
| 104 | - Extension((templates, mut ctx)): Extension<Template>, |
| 105 | - ) -> Result<Html<String>, Error> { |
| 106 | - ctx.insert("title", "discuss"); |
| 107 | - ctx.insert( |
| 108 | - "nav_elements", |
| 109 | - &navigation::global("", cfg.discuss_enabled()), |
| 110 | - ); |
| 111 | - ctx.insert("discnav", &util::navigation::discnav("overview")); |
| 112 | - match initiator.clone().client(InitiatorKind::Xmpp) { |
| 113 | - Some(_client) => {} |
| 114 | - None => {} |
| 115 | - }; |
| 116 | - match initiator.client(InitiatorKind::Mail) { |
| 117 | - Some(_client) => {} |
| 118 | - None => {} |
| 119 | - }; |
| 120 | - // ctx.insert("lists", &cfg.mail.unwrap().lists); |
| 121 | - let body = templates.render("discuss.html", &ctx)?; |
| 122 | - Ok(Html(body)) |
| 123 | - } |
| 124 | diff --git a/src/web2/routes/index.rs b/src/web2/routes/index.rs |
| 125 | index 3f6d7d3..77f109d 100644 |
| 126 | --- a/src/web2/routes/index.rs |
| 127 | +++ b/src/web2/routes/index.rs |
| 128 | @@ -103,10 +103,7 @@ pub async fn index( |
| 129 | } |
| 130 | ctx.insert("title", "ayllu"); |
| 131 | ctx.insert("collections", &collections); |
| 132 | - ctx.insert( |
| 133 | - "nav_elements", |
| 134 | - &navigation::global("", cfg.discuss_enabled()), |
| 135 | - ); |
| 136 | + ctx.insert("nav_elements", &navigation::global("", cfg.mail.is_some())); |
| 137 | let body = templates.render("index.html", &ctx)?; |
| 138 | Ok(Html(body)) |
| 139 | } |
| 140 | @@ -128,10 +125,7 @@ pub async fn collection( |
| 141 | let entry = entry.unwrap(); |
| 142 | let repositories = load_repositories(entry.path.as_str(), &db).await?; |
| 143 | ctx.insert("title", "ayllu"); |
| 144 | - ctx.insert( |
| 145 | - "nav_elements", |
| 146 | - &navigation::global("", cfg.discuss_enabled()), |
| 147 | - ); |
| 148 | + ctx.insert("nav_elements", &navigation::global("", cfg.mail.is_some())); |
| 149 | ctx.insert("collection", &entry.clone()); |
| 150 | ctx.insert("repositories", &repositories); |
| 151 | ctx.insert("is_hidden", &entry.hidden.is_some_and(|x| x)); |
| 152 | diff --git a/src/web2/routes/mail.rs b/src/web2/routes/mail.rs |
| 153 | index d835715..e08efa4 100644 |
| 154 | --- a/src/web2/routes/mail.rs |
| 155 | +++ b/src/web2/routes/mail.rs |
| 156 | @@ -9,13 +9,13 @@ use crate::config::Config; |
| 157 | use crate::web2::error::Error; |
| 158 | use crate::web2::middleware::rpc_initiator::{Initiator, Kind as InitiatorKind}; |
| 159 | use crate::web2::middleware::template::Template; |
| 160 | - use crate::web2::util; |
| 161 | use crate::web2::util::navigation; |
| 162 | use ayllu_api::mail_capnp::server::Client as MailClient; |
| 163 | |
| 164 | #[derive(Deserialize)] |
| 165 | pub struct Params { |
| 166 | pub list_id: String, |
| 167 | + pub thread_id: Option<String>, |
| 168 | pub message_id: Option<String>, |
| 169 | } |
| 170 | |
| 171 | @@ -44,8 +44,7 @@ pub async fn lists( |
| 172 | Extension((templates, mut ctx)): Extension<Template>, |
| 173 | ) -> Result<Html<String>, Error> { |
| 174 | ctx.insert("title", "lists"); |
| 175 | - ctx.insert("nav_elements", &navigation::global("dicsuss", true)); |
| 176 | - ctx.insert("discnav", &util::navigation::discnav("mail")); |
| 177 | + ctx.insert("nav_elements", &navigation::global("mail", true)); |
| 178 | // TODO: add stats method and display like xmpp |
| 179 | ctx.insert("lists", &cfg.mail.unwrap().lists.clone()); |
| 180 | // ctx.insert("lists", &cfg.mail.unwrap().lists); |
| 181 | @@ -70,9 +69,8 @@ pub async fn threads( |
| 182 | ))), |
| 183 | }?; |
| 184 | ctx.insert("title", &format!("list {}", list.address)); |
| 185 | - ctx.insert("nav_elements", &navigation::global("dicsuss", true)); |
| 186 | + ctx.insert("nav_elements", &navigation::global("mail", true)); |
| 187 | ctx.insert("list", list); |
| 188 | - ctx.insert("discnav", &util::navigation::discnav("mail")); |
| 189 | |
| 190 | let mail_client = initiator.client(InitiatorKind::Mail).unwrap(); |
| 191 | let mut threads = mail_client |
| 192 | @@ -99,6 +97,7 @@ pub async fn threads( |
| 193 | .await?; |
| 194 | threads.sort_by(|first, second| second.timestamp.cmp(&first.timestamp)); |
| 195 | ctx.insert("threads", &threads); |
| 196 | + ctx.insert("request_email", &list.request_address); |
| 197 | let body = templates.render("threads.html", &ctx)?; |
| 198 | Ok(Html(body)) |
| 199 | } |
| 200 | @@ -124,7 +123,7 @@ pub async fn thread( |
| 201 | let mut req = c.read_thread_request(); |
| 202 | req.get().set_id(params.list_id.as_str().into()); |
| 203 | req.get() |
| 204 | - .set_message_id(params.message_id.unwrap().as_str().into()); |
| 205 | + .set_message_id(params.thread_id.unwrap().as_str().into()); |
| 206 | let result = req.send().promise.await?; |
| 207 | for message in result.get()?.get_thread()? { |
| 208 | messages.push(Message { |
| 209 | @@ -140,8 +139,7 @@ pub async fn thread( |
| 210 | }) |
| 211 | .await?; |
| 212 | ctx.insert("title", &format!("list {}", list.address)); |
| 213 | - ctx.insert("nav_elements", &navigation::global("dicsuss", true)); |
| 214 | - ctx.insert("discnav", &util::navigation::discnav("mail")); |
| 215 | + ctx.insert("nav_elements", &navigation::global("mail", true)); |
| 216 | ctx.insert("list", list); |
| 217 | ctx.insert("list_id", &list.id); |
| 218 | ctx.insert("messages", &messages); |
| 219 | @@ -149,7 +147,7 @@ pub async fn thread( |
| 220 | Ok(Html(body)) |
| 221 | } |
| 222 | |
| 223 | - pub async fn post( |
| 224 | + pub async fn message( |
| 225 | Path(params): Path<Params>, |
| 226 | Extension(initiator): Extension<Initiator>, |
| 227 | Extension(cfg): Extension<Config>, |
| 228 | @@ -183,8 +181,7 @@ pub async fn post( |
| 229 | }) |
| 230 | .await?; |
| 231 | ctx.insert("title", &format!("list {}", list.address)); |
| 232 | - ctx.insert("nav_elements", &navigation::global("dicsuss", true)); |
| 233 | - ctx.insert("discnav", &util::navigation::discnav("mail")); |
| 234 | + ctx.insert("nav_elements", &navigation::global("mail", true)); |
| 235 | ctx.insert("list", list); |
| 236 | ctx.insert("list_id", &list.id); |
| 237 | ctx.insert("message", &message); |
| 238 | diff --git a/src/web2/routes/mod.rs b/src/web2/routes/mod.rs |
| 239 | index b3b25fc..ebb476d 100644 |
| 240 | --- a/src/web2/routes/mod.rs |
| 241 | +++ b/src/web2/routes/mod.rs |
| 242 | @@ -8,7 +8,6 @@ pub mod builds; |
| 243 | pub mod chart; |
| 244 | pub mod commit; |
| 245 | pub mod config; |
| 246 | - pub mod discuss; |
| 247 | pub mod finger; |
| 248 | pub mod index; |
| 249 | pub mod log; |
| 250 | diff --git a/src/web2/routes/xmpp.rs b/src/web2/routes/xmpp.rs |
| 251 | index 3187057..a2dac61 100644 |
| 252 | --- a/src/web2/routes/xmpp.rs |
| 253 | +++ b/src/web2/routes/xmpp.rs |
| 254 | @@ -9,7 +9,6 @@ use crate::web2::error::Error; |
| 255 | use crate::web2::extractors::config::ConfigReader; |
| 256 | use crate::web2::middleware::rpc_initiator::{Initiator, Kind as InitiatorKind}; |
| 257 | use crate::web2::middleware::template::Template; |
| 258 | - use crate::web2::util; |
| 259 | use crate::web2::util::navigation; |
| 260 | use ayllu_api::xmpp_capnp::server::Client as XmppClient; |
| 261 | |
| 262 | @@ -34,8 +33,7 @@ pub async fn channels( |
| 263 | Extension(initiator): Extension<Initiator>, |
| 264 | ) -> Result<Html<String>, Error> { |
| 265 | ctx.insert("title", "Discussions"); |
| 266 | - ctx.insert("nav_elements", &navigation::global("dicsuss", true)); |
| 267 | - ctx.insert("discnav", &util::navigation::discnav("xmpp")); |
| 268 | + ctx.insert("nav_elements", &navigation::global("xmpp", true)); |
| 269 | let xmpp_client = initiator.client(InitiatorKind::Xmpp).unwrap(); |
| 270 | let channels = xmpp_client |
| 271 | .invoke(move |c: XmppClient| async move { |
| 272 | @@ -72,8 +70,7 @@ pub async fn channel( |
| 273 | Extension(initiator): Extension<Initiator>, |
| 274 | ) -> Result<Html<String>, Error> { |
| 275 | ctx.insert("title", "lists"); |
| 276 | - ctx.insert("nav_elements", &navigation::global("dicsuss", true)); |
| 277 | - ctx.insert("discnav", &util::navigation::discnav("xmpp")); |
| 278 | + ctx.insert("nav_elements", &navigation::global("xmpp", true)); |
| 279 | ctx.insert("channel", ¶ms.channel); |
| 280 | let xmpp_client = initiator.client(InitiatorKind::Xmpp).unwrap(); |
| 281 | let messages = xmpp_client |
| 282 | diff --git a/src/web2/server.rs b/src/web2/server.rs |
| 283 | index 653173b..1ede61a 100644 |
| 284 | --- a/src/web2/server.rs |
| 285 | +++ b/src/web2/server.rs |
| 286 | @@ -30,7 +30,6 @@ use crate::web2::routes::builds; |
| 287 | use crate::web2::routes::chart; |
| 288 | use crate::web2::routes::commit; |
| 289 | use crate::web2::routes::config; |
| 290 | - use crate::web2::routes::discuss; |
| 291 | use crate::web2::routes::finger; |
| 292 | use crate::web2::routes::index; |
| 293 | use crate::web2::routes::log as log_route; |
| 294 | @@ -171,48 +170,35 @@ pub async fn serve(cfg: &Config) -> Result<(), Box<dyn Error>> { |
| 295 | )), |
| 296 | ) |
| 297 | .nest( |
| 298 | - "/discuss", |
| 299 | + "/mail", |
| 300 | Router::new() |
| 301 | - .route("/", routing::get(discuss::serve)) |
| 302 | + .route("/", routing::get(mail::lists)) |
| 303 | + .route("/:list_id", routing::get(mail::threads)) |
| 304 | + .route("/:list_id/thread/:thread_id", routing::get(mail::thread)) |
| 305 | + .route("/:list_id/message/:message_id", routing::get(mail::message)) |
| 306 | .layer(from_fn_with_state( |
| 307 | - Arc::new(cfg.clone()), |
| 308 | - rpc_initiator::optional, |
| 309 | + Arc::new((cfg.clone(), templates.clone(), mail_required_plugins)), |
| 310 | + rpc_initiator::required, |
| 311 | )) |
| 312 | .layer(from_fn_with_state( |
| 313 | Arc::new((cfg.clone(), templates.clone())), |
| 314 | template::middleware, |
| 315 | + )), |
| 316 | + ) |
| 317 | + .nest( |
| 318 | + "/xmpp", |
| 319 | + Router::new() |
| 320 | + .route("/", routing::get(xmpp::channels)) |
| 321 | + .route("/:channel", routing::get(xmpp::channel)) |
| 322 | + .route("/:channel/:last_message", routing::get(xmpp::channel)) |
| 323 | + .layer(from_fn_with_state( |
| 324 | + Arc::new((cfg.clone(), templates.clone(), xmpp_required_plugins)), |
| 325 | + rpc_initiator::required, |
| 326 | )) |
| 327 | - .nest( |
| 328 | - "/mail", |
| 329 | - Router::new() |
| 330 | - .route("/", routing::get(mail::lists)) |
| 331 | - .route("/:list_id", routing::get(mail::threads)) |
| 332 | - .route("/:list_id/:message_id", routing::get(mail::thread)) |
| 333 | - .route("/post/:list_id/:message_id", routing::get(mail::post)) |
| 334 | - .layer(from_fn_with_state( |
| 335 | - Arc::new((cfg.clone(), templates.clone(), mail_required_plugins)), |
| 336 | - rpc_initiator::required, |
| 337 | - )) |
| 338 | - .layer(from_fn_with_state( |
| 339 | - Arc::new((cfg.clone(), templates.clone())), |
| 340 | - template::middleware, |
| 341 | - )), |
| 342 | - ) |
| 343 | - .nest( |
| 344 | - "/xmpp", |
| 345 | - Router::new() |
| 346 | - .route("/", routing::get(xmpp::channels)) |
| 347 | - .route("/:channel", routing::get(xmpp::channel)) |
| 348 | - .route("/:channel/:last_message", routing::get(xmpp::channel)) |
| 349 | - .layer(from_fn_with_state( |
| 350 | - Arc::new((cfg.clone(), templates.clone(), xmpp_required_plugins)), |
| 351 | - rpc_initiator::required, |
| 352 | - )) |
| 353 | - .layer(from_fn_with_state( |
| 354 | - Arc::new((cfg.clone(), templates.clone())), |
| 355 | - template::middleware, |
| 356 | - )), |
| 357 | - ), |
| 358 | + .layer(from_fn_with_state( |
| 359 | + Arc::new((cfg.clone(), templates.clone())), |
| 360 | + template::middleware, |
| 361 | + )), |
| 362 | ) |
| 363 | .nest( |
| 364 | "/:collection/:name", |
| 365 | diff --git a/src/web2/util.rs b/src/web2/util.rs |
| 366 | index 59de08c..79b1f3e 100644 |
| 367 | --- a/src/web2/util.rs |
| 368 | +++ b/src/web2/util.rs |
| 369 | @@ -3,7 +3,6 @@ use std::path::PathBuf; |
| 370 | use axum::http::Uri; |
| 371 | use url::Url; |
| 372 | |
| 373 | - |
| 374 | // select a segment of the path from the given url between start and end. |
| 375 | // e.g. |
| 376 | // http://fuu.bar/baz/qux 0 1 -> Some(/baz) |
| 377 | @@ -60,7 +59,7 @@ pub mod navigation { |
| 378 | |
| 379 | pub type Items = Vec<(String, String, bool)>; |
| 380 | |
| 381 | - pub fn global(current_page: &str, discuss_visible: bool) -> Items { |
| 382 | + pub fn global(current_page: &str, mail_visible: bool) -> Items { |
| 383 | let mut nav: Items = vec![ |
| 384 | ( |
| 385 | String::from("about"), |
| 386 | @@ -73,11 +72,11 @@ pub mod navigation { |
| 387 | current_page == "config", |
| 388 | ), |
| 389 | ]; |
| 390 | - if discuss_visible { |
| 391 | + if mail_visible { |
| 392 | nav.push(( |
| 393 | - String::from("discuss"), |
| 394 | - String::from("/discuss"), |
| 395 | - current_page == "discuss", |
| 396 | + String::from("mail"), |
| 397 | + String::from("/mail"), |
| 398 | + current_page == "mail", |
| 399 | )) |
| 400 | } |
| 401 | nav |
| 402 | @@ -193,26 +192,6 @@ pub mod navigation { |
| 403 | ), |
| 404 | ] |
| 405 | } |
| 406 | - |
| 407 | - pub fn discnav(current_page: &str) -> Items { |
| 408 | - vec![ |
| 409 | - ( |
| 410 | - String::from("overview"), |
| 411 | - String::from("/discuss"), |
| 412 | - current_page == "overview", |
| 413 | - ), |
| 414 | - ( |
| 415 | - String::from("mail"), |
| 416 | - String::from("/discuss/mail"), |
| 417 | - current_page == "mail", |
| 418 | - ), |
| 419 | - ( |
| 420 | - String::from("xmpp"), |
| 421 | - String::from("/discuss/xmpp"), |
| 422 | - current_page == "xmpp", |
| 423 | - ), |
| 424 | - ] |
| 425 | - } |
| 426 | } |
| 427 | |
| 428 | const UNIT: f64 = 1024.0; |
| 429 | diff --git a/themes/default/templates/discuss.html b/themes/default/templates/discuss.html |
| 430 | deleted file mode 100644 |
| 431 | index ae080ac..0000000 |
| 432 | --- a/themes/default/templates/discuss.html |
| 433 | +++ /dev/null |
| 434 | @@ -1,11 +0,0 @@ |
| 435 | - {% import "macros.html" as macros %} |
| 436 | - {% extends "base.html" %} |
| 437 | - {% block content %} |
| 438 | - <section> |
| 439 | - <article> |
| 440 | - <header> |
| 441 | - {{ macros::navigation(items=discnav, title="Discussions") }} |
| 442 | - </header> |
| 443 | - </article> |
| 444 | - </section> |
| 445 | - {% endblock %} |
| 446 | diff --git a/themes/default/templates/lists.html b/themes/default/templates/lists.html |
| 447 | index bdb32ba..0196b96 100644 |
| 448 | --- a/themes/default/templates/lists.html |
| 449 | +++ b/themes/default/templates/lists.html |
| 450 | @@ -4,7 +4,7 @@ |
| 451 | <section> |
| 452 | <article> |
| 453 | <header> |
| 454 | - {{ macros::navigation(items=discnav, title="Mailing Lists") }} |
| 455 | + <h1> Mailing Lists </h1> |
| 456 | </header> |
| 457 | <table> |
| 458 | <thead> |
| 459 | @@ -16,9 +16,9 @@ |
| 460 | <tbody> |
| 461 | {% for list in lists %} |
| 462 | <tr> |
| 463 | - <td><a href="/discuss/mail/{{list.id}}">{{ list.id }}</a></td> |
| 464 | + <td>{{ list.id }}</td> |
| 465 | <td>{{ list.name }}</td> |
| 466 | - <td>{{ list.description }}</td> |
| 467 | + <td><a href="/mail/{{list.id}}">{{ list.description }}</a></td> |
| 468 | <td>{{ list.address }}</td> |
| 469 | </tr> |
| 470 | {% endfor %} |
| 471 | diff --git a/themes/default/templates/thread.html b/themes/default/templates/thread.html |
| 472 | index 764f9b7..d0005e8 100644 |
| 473 | --- a/themes/default/templates/thread.html |
| 474 | +++ b/themes/default/templates/thread.html |
| 475 | @@ -7,7 +7,7 @@ |
| 476 | <header> |
| 477 | <b>From: {{ reply.from_address }}</b></br> |
| 478 | <b>To: ???</b></br> |
| 479 | - <b><a href="/discuss/mail/post/{{list_id}}/{{reply.message_id}}">{{ reply.message_id }}</a></b> |
| 480 | + <b><a href="/mail/{{list_id}}/message/{{reply.message_id}}">{{ reply.message_id }}</a></b> |
| 481 | <span class="right">{{ reply.created_at | format_epoch }}</span> |
| 482 | </header> |
| 483 | <pre>{{ reply.text }}</pre> |
| 484 | diff --git a/themes/default/templates/threads.html b/themes/default/templates/threads.html |
| 485 | index d7c7431..c7f8cfb 100644 |
| 486 | --- a/themes/default/templates/threads.html |
| 487 | +++ b/themes/default/templates/threads.html |
| 488 | @@ -4,8 +4,25 @@ |
| 489 | <section> |
| 490 | <article> |
| 491 | <header> |
| 492 | - {{ macros::navigation(items=discnav, title=list.id) }} |
| 493 | + <h1> {{ list.name }} </h1> |
| 494 | </header> |
| 495 | + <div class="mailing-list-details"> |
| 496 | + <h4> {{ list.description }} </h4> |
| 497 | + </br> |
| 498 | + <span class="labels"> |
| 499 | + {% for topic in list.topics %} |
| 500 | + <span class="feature">{{topic}}</span> |
| 501 | + {% endfor %} |
| 502 | + </br><b>Post Policy = {{list.post_policy}} </b> |
| 503 | + </br><b>Subscription Policy = {{list.subscription_policy.kind}}</b> |
| 504 | + </br><b>Send Confirmation = {{list.subscription_policy.send_confirmation}}</b> |
| 505 | + </br> |
| 506 | + </span></br> |
| 507 | + <h4> Subscribe </h4> |
| 508 | + <p> Send an e-mail to <a href="mailto:{{ request_email }}?subject=subscribe">{{request_email}}</a> with the following subject: <code>subscribe</code> </p> |
| 509 | + <h4> Unsubscribe </h4> |
| 510 | + <p> Send an e-mail to <a href="mailto:{{ request_email }}?subject=unsubscribe">{{request_email}}</a> with the following subject: <code>unsubscribe</code> </p> |
| 511 | + </div> |
| 512 | <table> |
| 513 | <thead> |
| 514 | <th> from </th> |
| 515 | @@ -18,7 +35,7 @@ |
| 516 | <tr> |
| 517 | <td>{{ thread.from }}</a></td> |
| 518 | <td>{{thread.timestamp | format_epoch }}</td> |
| 519 | - <td><a href="/discuss/mail/{{list.id}}/{{thread.message_id}}">{{thread.subject}}</a></td> |
| 520 | + <td><a href="/mail/{{list.id}}/thread/{{thread.message_id}}">{{thread.subject}}</a></td> |
| 521 | <td>{{thread.n_replies}}</td> |
| 522 | </tr> |
| 523 | {% endfor %} |