forge
ExploreLog in

Tahinli/forge

public
0 watching
★ 0
3.0 KiB
1//! The Discussions switch on the repo settings page. `POST /{owner}/{repo}/settings/discussions`
2//! with `enabled=1` or `enabled=0`. Admins only. Turning discussions off hides the tab and makes
3//! the discussion pages 404; the discussions themselves are kept.
4
5use forge_core::AuditAction;
6use serde::Deserialize;
7use topcoat::Result;
8use topcoat::context::{Cx, app_context};
9use topcoat::router::content::Form;
10use topcoat::router::error::{SeeOther, forbidden, see_other};
11use topcoat::router::{path_param, route};
12use topcoat::view::{View, component, view};
13
14use crate::audit::{self, Actor, Target};
15use crate::auth::require_csrf;
16use crate::i18n::Lang;
17use crate::i18n::community::{self as g, K};
18use crate::pages::repo_ctx::load_repo;
19use crate::state::AppState;
20use crate::views::repo_href;
21
22path_param!(owner);
23path_param!(repo);
24
25/// The settings card with the on/off button. `enabled` is the current state.
26#[component]
27pub(crate) async fn discussions_card(
28 lang: Lang,
29 action: String,
30 csrf: String,
31 enabled: bool,
32) -> Result<impl View> {
33 Ok(view! {
34 <section class="card" id="discussions">
35 <h2>(g::t(lang, K::Discussions))</h2>
36 <p class="muted small">(g::t(lang, K::DiscussionsDescription))</p>
37 <form method="post" action=(action.as_str()) class="inline-form">
38 <input type="hidden" name="csrf" value=(csrf.as_str())>
39 if enabled {
40 <input type="hidden" name="enabled" value="0">
41 <button type="submit">(g::t(lang, K::TurnOffDiscussions))</button>
42 } else {
43 <input type="hidden" name="enabled" value="1">
44 <button type="submit">(g::t(lang, K::TurnOnDiscussions))</button>
45 }
46 </form>
47 </section>
48 })
49}
50
51#[derive(Deserialize, Default)]
52struct ToggleForm {
53 #[serde(default)]
54 csrf: String,
55 #[serde(default)]
56 enabled: String,
57}
58
59#[route(POST "/{owner}/{repo}/settings/discussions")]
60async fn settings_discussions_save(cx: &Cx, Form(form): Form<ToggleForm>) -> Result<SeeOther> {
61 let ctx = load_repo(cx, path_param::<Owner>(cx), path_param::<Repo>(cx)).await?;
62 let (user, _) = require_csrf(cx, &form.csrf).await?;
63 if !ctx.can_admin() {
64 return Err(forbidden().into());
65 }
66 let enabled = form.enabled == "1";
67 let state = app_context::<AppState>(cx);
68 state
69 .store
70 .set_discussions_enabled(&ctx.repo.id, enabled)
71 .await?;
72 let name = format!("{}/{}", ctx.repo.owner_name, ctx.repo.name);
73 let action = if enabled {
74 AuditAction::DiscussionsEnabled
75 } else {
76 AuditAction::DiscussionsDisabled
77 };
78 audit::record(
79 &state.store,
80 Some(cx),
81 Actor::User(&user),
82 action,
83 Target {
84 kind: "repo",
85 id: &ctx.repo.id,
86 name: &name,
87 },
88 serde_json::Value::Null,
89 )
90 .await;
91 let back = repo_href(&ctx.repo.owner_name, &ctx.repo.name, "settings", "");
92 Ok(see_other(format!("{back}#discussions")))
93}