M res/lang/en.ftl => res/lang/en.ftl +3 -0
@@ 9,7 9,9 @@ action_flag = flag
add = Add
add_by_remote_id = Add by ID:
administration = Administration
+administration_edit = Edit Instance Details
administration_signup_allowed = Signups are currently
+administration_edit_signup_allowed = Public Signups:
all = All
all_title = The Whole Known Network
allowed_false = not allowed
@@ 101,6 103,7 @@ new_post_poll = Add Poll
no_cancel = No, cancel
no_such_invitation = No such invitation
no_such_local_user = No such local user
+not_site_admin = You are not an instance admin
nothing = Looks like there's nothing here.
nothing_yet = Looks like there's nothing here (yet!).
notifications = Notifications
M src/components/mod.rs => src/components/mod.rs +16 -2
@@ 680,13 680,27 @@ pub fn MaybeFillCheckbox<'a, M: GetIndex<&'a str, serde_json::Value>>(
#[render::component]
pub fn MaybeFillOption<'a, M: GetIndex<&'a str, serde_json::Value>, Children: render::Render>(
values: &'a Option<&'a M>,
+ default_value: Option<&'a str>,
name: &'a str,
value: &'a str,
children: Children,
) {
- let selected_value = maybe_fill_value(values, name, None);
+ let selected_value = maybe_fill_value(values, name, default_value);
+
+ SelectOption {
+ value,
+ selected: selected_value == value,
+ children,
+ }
+}
- if selected_value == value {
+#[render::component]
+pub fn SelectOption<'a, Children: render::Render>(
+ value: &'a str,
+ selected: bool,
+ children: Children,
+) {
+ if selected {
render::rsx! {
<option value={value} selected={""}>{children}</option>
}
M src/main.rs => src/main.rs +8 -0
@@ 160,6 160,14 @@ pub fn get_lang_for_req(req: &hyper::Request<hyper::Body>) -> Translator {
get_lang_for_headers(req.headers())
}
+pub fn bool_as_str(src: bool) -> &'static str {
+ if src {
+ "true"
+ } else {
+ "false"
+ }
+}
+
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
M src/routes/administration.rs => src/routes/administration.rs +163 -7
@@ 1,13 1,12 @@
use super::{
fetch_base_data, for_client, get_cookie_map_for_headers, get_cookie_map_for_req, html_response,
- res_to_error,
+ res_to_error, CookieMap,
};
-use crate::components::HTPage;
+use crate::components::{HTPage, MaybeFillOption};
use crate::lang;
use crate::resp_types::RespInstanceInfo;
-use serde_derive::Deserialize;
+use std::collections::HashMap;
use std::convert::TryInto;
-use std::ops::Deref;
use std::sync::Arc;
async fn page_administration(
@@ 21,6 20,19 @@ async fn page_administration(
let base_data =
fetch_base_data(&ctx.backend_host, &ctx.http_client, req.headers(), &cookies).await?;
+ let title = lang.tr(&lang::ADMINISTRATION);
+
+ if !base_data.is_site_admin() {
+ return Ok(html_response(render::html! {
+ <HTPage base_data={&base_data} lang={&lang} title={&title}>
+ <h1>{title.as_ref()}</h1>
+ <div class={"errorBox"}>
+ {lang.tr(&lang::not_site_admin())}
+ </div>
+ </HTPage>
+ }));
+ }
+
let api_res = res_to_error(
ctx.http_client
.get(
@@ 34,11 46,10 @@ async fn page_administration(
let api_res = hyper::body::to_bytes(api_res.into_body()).await?;
let api_res: RespInstanceInfo = serde_json::from_slice(&api_res)?;
- let title = lang.tr(&lang::ADMINISTRATION);
-
Ok(html_response(render::html! {
<HTPage base_data={&base_data} lang={&lang} title={&title}>
<h1>{title.as_ref()}</h1>
+ <a href={"/administration/edit"}>{lang.tr(&lang::administration_edit())}</a>
<ul>
<li>
{lang.tr(&lang::ADMINISTRATION_SIGNUP_ALLOWED)}{" "}
@@ 53,6 64,151 @@ async fn page_administration(
}))
}
+async fn page_administration_edit(
+ _params: (),
+ ctx: Arc<crate::RouteContext>,
+ req: hyper::Request<hyper::Body>,
+) -> Result<hyper::Response<hyper::Body>, crate::Error> {
+ let cookies = get_cookie_map_for_req(&req)?;
+
+ page_administration_edit_inner(req.headers(), &cookies, ctx, None, None).await
+}
+
+async fn page_administration_edit_inner(
+ headers: &hyper::header::HeaderMap,
+ cookies: &CookieMap<'_>,
+ ctx: Arc<crate::RouteContext>,
+ display_error: Option<String>,
+ prev_values: Option<&HashMap<&str, serde_json::Value>>,
+) -> Result<hyper::Response<hyper::Body>, crate::Error> {
+ let lang = crate::get_lang_for_headers(headers);
+
+ let base_data = fetch_base_data(&ctx.backend_host, &ctx.http_client, headers, cookies).await?;
+
+ let title = lang.tr(&lang::ADMINISTRATION_EDIT);
+
+ if !base_data.is_site_admin() {
+ return Ok(html_response(render::html! {
+ <HTPage base_data={&base_data} lang={&lang} title={&title}>
+ <h1>{title.as_ref()}</h1>
+ <div class={"errorBox"}>
+ {lang.tr(&lang::not_site_admin())}
+ </div>
+ </HTPage>
+ }));
+ }
+
+ let api_res = res_to_error(
+ ctx.http_client
+ .get(
+ format!("{}/api/unstable/instance", ctx.backend_host)
+ .try_into()
+ .unwrap(),
+ )
+ .await?,
+ )
+ .await?;
+ let api_res = hyper::body::to_bytes(api_res.into_body()).await?;
+ let api_res: RespInstanceInfo = serde_json::from_slice(&api_res)?;
+
+ let signup_allowed_value = Some(crate::bool_as_str(api_res.signup_allowed));
+
+ Ok(html_response(render::html! {
+ <HTPage base_data={&base_data} lang={&lang} title={&title}>
+ <h1>{title.as_ref()}</h1>
+ {
+ display_error.map(|msg| {
+ render::rsx! {
+ <div class={"errorBox"}>{msg}</div>
+ }
+ })
+ }
+ <form method={"POST"} action={"/administration/edit/submit"}>
+ <label>
+ {lang.tr(&lang::administration_edit_signup_allowed())}<br />
+ <select name={"signup_allowed"}>
+ <MaybeFillOption value={"true"} values={&prev_values} default_value={signup_allowed_value} name={"signup_allowed"}>
+ {lang.tr(&lang::allowed_true())}
+ </MaybeFillOption>
+ <MaybeFillOption value={"false"} values={&prev_values} default_value={signup_allowed_value} name={"signup_allowed"}>
+ {lang.tr(&lang::allowed_false())}
+ </MaybeFillOption>
+ </select>
+ </label>
+ <br />
+ <br />
+ <button type={"submit"}>{"Save"}</button>
+ </form>
+ </HTPage>
+ }))
+}
+
+async fn handler_administration_edit_submit(
+ _params: (),
+ ctx: Arc<crate::RouteContext>,
+ req: hyper::Request<hyper::Body>,
+) -> Result<hyper::Response<hyper::Body>, crate::Error> {
+ let (req_parts, body) = req.into_parts();
+
+ let cookies = get_cookie_map_for_headers(&req_parts.headers)?;
+
+ let body = hyper::body::to_bytes(body).await?;
+ let mut body: HashMap<&str, serde_json::Value> = serde_urlencoded::from_bytes(&body)?;
+
+ body.insert(
+ "signup_allowed",
+ body.get("signup_allowed")
+ .and_then(|x| x.as_str())
+ .ok_or(crate::Error::InternalStrStatic(
+ "Failed to extract signup_allowed in administration edit",
+ ))?
+ .parse()?,
+ );
+
+ let api_res = res_to_error(
+ ctx.http_client
+ .request(for_client(
+ hyper::Request::patch(format!("{}/api/unstable/instance", ctx.backend_host,))
+ .body(serde_json::to_vec(&body)?.into())?,
+ &req_parts.headers,
+ &cookies,
+ )?)
+ .await?,
+ )
+ .await;
+
+ match api_res {
+ Err(crate::Error::RemoteError((_, message))) => {
+ page_administration_edit_inner(
+ &req_parts.headers,
+ &cookies,
+ ctx,
+ Some(message),
+ Some(&body),
+ )
+ .await
+ }
+ Err(other) => Err(other),
+ Ok(_) => Ok(hyper::Response::builder()
+ .status(hyper::StatusCode::SEE_OTHER)
+ .header(hyper::header::LOCATION, "/administration")
+ .body("Successfully edited.".into())?),
+ }
+}
+
pub fn route_administration() -> crate::RouteNode<()> {
- crate::RouteNode::new().with_handler_async(hyper::Method::GET, page_administration)
+ crate::RouteNode::new()
+ .with_handler_async(hyper::Method::GET, page_administration)
+ .with_child(
+ "edit",
+ crate::RouteNode::new()
+ .with_handler_async(hyper::Method::GET, page_administration_edit)
+ .with_child(
+ "submit",
+ crate::RouteNode::new().with_handler_async(
+ hyper::Method::POST,
+ handler_administration_edit_submit,
+ ),
+ ),
+ )
}
M src/routes/communities.rs => src/routes/communities.rs +3 -3
@@ 1371,9 1371,9 @@ async fn page_community_new_post_inner(
{" "}
<input type={"number"} name={"poll_duration_value"} required={""} value={maybe_fill_value(&prev_values, "poll_duration_value", Some("10"))} />
<select name={"poll_duration_unit"}>
- <MaybeFillOption values={&prev_values} name={"poll_duration_unit"} value={"m"}>{lang.tr(&lang::time_input_minutes())}</MaybeFillOption>
- <MaybeFillOption values={&prev_values} name={"poll_duration_unit"} value={"h"}>{lang.tr(&lang::time_input_hours())}</MaybeFillOption>
- <MaybeFillOption values={&prev_values} name={"poll_duration_unit"} value={"d"}>{lang.tr(&lang::time_input_days())}</MaybeFillOption>
+ <MaybeFillOption default_value={None} values={&prev_values} name={"poll_duration_unit"} value={"m"}>{lang.tr(&lang::time_input_minutes())}</MaybeFillOption>
+ <MaybeFillOption default_value={None} values={&prev_values} name={"poll_duration_unit"} value={"h"}>{lang.tr(&lang::time_input_hours())}</MaybeFillOption>
+ <MaybeFillOption default_value={None} values={&prev_values} name={"poll_duration_unit"} value={"d"}>{lang.tr(&lang::time_input_days())}</MaybeFillOption>
</select>
</div>
</div>