diff --git a/src/hedgedoc.rs b/src/hedgedoc.rs index 3e89651..66be2df 100644 --- a/src/hedgedoc.rs +++ b/src/hedgedoc.rs @@ -59,20 +59,27 @@ impl HedgeDoc { if response.status().is_success() { Ok(response) } else { - Err(format!("Failed to connect to hedgedoc server: HTTP status code {}", response.status()).into()) + Err(format!( + "Failed to connect to hedgedoc server: HTTP status code {}", + response.status() + ) + .into()) } - } + }, Err(e) => { if e.is_connect() { Err("Failed to connect to hedgedoc server. Please check your internet connection or the server URL.".into()) } else { - Err(format!("An error occurred while sending the request to the hedgedoc server: {}", e).into()) + Err(format!( + "An error occurred while sending the request to the hedgedoc server: {}", + e + ) + .into()) } - } + }, } } - fn get_id_from_response(&self, res: Response) -> String { res.url().to_string().trim_start_matches(&format!("{}/", self.server_url)).to_string() } @@ -122,7 +129,8 @@ pub fn strip_metadata(pad_content: String) -> String { let re_yaml = Regex::new(r"(?s)---\s*.*?\s*(?:\.\.\.|---)").unwrap(); let pad_content = re_yaml.replace_all(&pad_content, "").to_string(); let re_comment = Regex::new(r"(?s)").unwrap(); - re_comment.replace_all(&pad_content, "").to_string() + let content_without_comments = re_comment.replace_all(&pad_content, "").to_string(); + content_without_comments.trim().to_string() } pub fn summarize(pad_content: String) -> String { diff --git a/src/main.rs b/src/main.rs index 44c2687..f46a1b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,11 +7,6 @@ use std::io::IsTerminal; use std::os::linux::raw::stat; use std::time::Instant; -use chrono::{Local, NaiveDate, Utc, DateTime}; -use clap::{Arg, Command}; -use colored::Colorize; -use regex::Regex; -use cccron_lib::{trace_var, trace_var_, verboseln}; use cccron_lib::config_spec::{self, CfgField, CfgGroup, CfgSpec}; use cccron_lib::date; use cccron_lib::email::{self, Email, SimpleEmail}; @@ -20,6 +15,11 @@ use cccron_lib::is_dry_run; use cccron_lib::key_value::{KeyValueStore as KV, KeyValueStore}; use cccron_lib::mediawiki::{self, Mediawiki}; use cccron_lib::NYI; +use cccron_lib::{trace_var, trace_var_, verboseln}; +use chrono::{DateTime, Local, NaiveDate, Utc}; +use clap::{Arg, Command}; +use colored::Colorize; +use regex::Regex; /* ***** Config Spec ***** */ const CONFIG_SPEC: CfgSpec<'static> = CfgSpec { @@ -93,12 +93,12 @@ fn today() -> NaiveDate { } /// Gets either the state from the config or overrides it with the state from /// the environment variable `STATE_OVERRIDE` (for testing purposes.) -/// +/// /// For example, `STATE_OVERRIDE=Waiting` can be used to debug mediawiki. fn state(config: &KeyValueStore) -> ProgramState { match env::var("STATE_OVERRIDE") { Ok(val) => ProgramState::parse(&val), - Err(_e) => ProgramState::parse(&config["state-name"]) + Err(_e) => ProgramState::parse(&config["state-name"]), } } @@ -178,7 +178,7 @@ fn main() -> Result<(), Box> { &config["wiki-http-password"], &config["wiki-api-user"], &config["wiki-api-secret"], - false, // is_dry_run(), // TODO: Remove this false in order for actually letting dry_run affecting if mediawiki is run + false, // is_dry_run(), // TODO: Remove this false in order for actually letting dry_run affecting if mediawiki is run &config["wiki-plenum-page"], ); trace_var_!(wiki); @@ -475,20 +475,34 @@ fn do_protocol( let human_date = plenum_day.format("%d.%m.%Y"); let pad_content = hedgedoc::strip_metadata(pad_content); let subject = format!("Protokoll vom Plenum am {human_date}"); - NYI!("link for next plenum"); - NYI!("replace [toc] with actual table of contents"); + let pad_content = pad_content.replace("[toc]", &toc); let body = format!( "Anbei das Protokoll vom {human_date}, ab sofort auch im Wiki zu finden.\n\n\ - Das Pad für das nächste Plenum ist zu finden unter {}.\n\n-----\n\n{pad_content}", - "" + Das Pad für das nächste Plenum ist zu finden unter <{}/{}>.\nDie Protokolle der letzten Plena findet ihr im wiki unter <{}/index.php?title={}>.\n\n---Protokoll:---\n{pad_content}\n-----", + &config["hedgedoc-server-url"], + &config["hedgedoc-next-id"], + &config["wiki-server-url"], + &config["wiki-plenum-page"] ); let _message_id = send_email(&subject, &body, email, config)?; - NYI!("convert to mediawiki"); mediawiki::pad_ins_wiki(pad_content, wiki, plenum_day)?; - NYI!("add to wiki"); config.set("state-name", &ProgramState::Logged.to_string()).ok(); } else { - NYI!("What do we do in the no topics / no plenum case?"); + let human_date = plenum_day.format("%d.%m.%Y"); + let pad_content = hedgedoc::strip_metadata(pad_content); + let subject = format!("Protokoll vom ausgefallenem Plenum am {human_date}"); + let pad_content = pad_content.replace("[toc]", &toc); + let body = format!( + "Das letzte Plenum hatte Anbei das Protokoll vom {human_date}, ab sofort auch im Wiki zu finden.\n\n\ + Das Pad für das nächste Plenum ist zu finden unter {}/{}.\nDie Protokolle der letzten Plena findet ihr im wiki unter {}/index.php?title={}.\n\n---Protokoll:---{pad_content}", + &config["hedgedoc-server-url"], + &config["hedgedoc-next-id"], + &config["wiki-server-url"], + &config["wiki-plenum-page"] + ); + let _message_id = send_email(&subject, &body, email, config)?; + mediawiki::pad_ins_wiki(pad_content, wiki, plenum_day)?; + config.set("state-name", &ProgramState::Logged.to_string()).ok(); } Ok(()) } @@ -683,5 +697,5 @@ fn display_logo(eta: &str) { ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ "#; let ansi_art = format!("{ansi_art_pt1}{eta}{ansi_art_pt2}"); - println!("{}", ansi_art ); + println!("{}", ansi_art); } diff --git a/src/mediawiki.rs b/src/mediawiki.rs index 99092b1..b7b761a 100644 --- a/src/mediawiki.rs +++ b/src/mediawiki.rs @@ -7,8 +7,8 @@ use reqwest::blocking::Client; use serde::Deserialize; use serde_json::{json, Value}; -use crate::{trace_var, verboseln}; use crate::config_spec::{CfgField, CfgGroup}; +use crate::{trace_var, verboseln}; pub const CONFIG: CfgGroup<'static> = CfgGroup { name: "wiki", @@ -24,10 +24,7 @@ pub const CONFIG: CfgGroup<'static> = CfgGroup { default: "cccb-wiki", description: "HTTP basic auth user name.", }, - CfgField::Password { - key: "http-password", - description: "HTTP basic auth password." - }, + CfgField::Password { key: "http-password", description: "HTTP basic auth password." }, CfgField::Default { key: "api-user", default: "PlenumBot@PlenumBot-PW2", @@ -45,8 +42,8 @@ pub const CONFIG: CfgGroup<'static> = CfgGroup { CfgField::Default { key: "eta", default: "no ETA, program never ran", - description: "ETA message for estimating time the program takes." - } + description: "ETA message for estimating time the program takes.", + }, ], }; @@ -81,9 +78,16 @@ pub enum ValidRequestTypes { PostForEditing } +pub enum ValidPageEdits { + WithPotentiallyOverriding, + WithoutOverriding, + ModifyPlenumPageAfterwards_WithoutOverriding +} + impl Mediawiki { pub fn new( - server_url: &str, http_auth_user: &str, http_auth_password: &str, api_user: &str, api_secret: &str, is_dry_run: bool, plenum_main_page_name: &str, + server_url: &str, http_auth_user: &str, http_auth_password: &str, api_user: &str, + api_secret: &str, is_dry_run: bool, plenum_main_page_name: &str, ) -> Self { Self { server_url: server_url.to_string(), @@ -98,28 +102,29 @@ impl Mediawiki { client: Client::builder().cookie_store(true).build().unwrap(), } } - pub fn login (&self) -> Result<(), Box> { + pub fn login(&self) -> Result<(), Box> { let url = format!("{}/api.php?", self.server_url); // retrieve login token first let params_0: Box<[(&str, &str)]> = Box::from([ ("action", "query"), ("meta", "tokens"), ("type", "login"), - ("format", "json") + ("format", "json"), ]); verboseln!("Login params: {:?}", params_0); - let resp_0: String = self.make_request(url.clone(), params_0, ValidRequestTypes::Get)?; + let resp_0: String = self.make_request(url.clone(), params_0, ValidRequestTypes::Get)?; verboseln!("Raw response login_0: {}", resp_0.yellow()); let resp_0_deserialized: serde_json::Value = serde_json::from_str(&resp_0)?; verboseln!("login0 deserialized"); - + let login_token = resp_0_deserialized .pointer("/query/tokens/logintoken") .and_then(|v| v.as_str()) - .map(|token| token.replace("+\\", "")) .ok_or("Login token not found")?; - self.login_token.set(login_token)?; + verboseln!("Value of login token: {login_token}"); + + self.login_token.set(login_token.to_string())?; verboseln!("login0 finished"); let login_token: String = self.login_token.clone().into_inner().unwrap(); @@ -129,15 +134,16 @@ impl Mediawiki { ("lgname", &self.api_user), ("lgpassword", &self.api_secret), ("lgtoken", &login_token), - ("format", "json") + ("format", "json"), ]); verboseln!("Login params: {:?}", params_1); - let resp_1: String = self.client - .post(&url) - // .basic_auth(&self.http_user, Some(&self.http_password)) // TODO: ZU TESTZWECKEN ENTFERNT - .form(¶ms_1) - .send()? - .text()?; + let resp_1: String = self + .client + .post(&url) + // .basic_auth(&self.http_user, Some(&self.http_password)) // TODO: ZU TESTZWECKEN ENTFERNT + .form(¶ms_1) + .send()? + .text()?; verboseln!("Raw response login_1: {}", resp_1.yellow()); let resp_1_deserialized: serde_json::Value = serde_json::from_str(&resp_1)?; if let Some(result) = resp_1_deserialized.get("login").and_then(|l| l.get("result")) { @@ -151,14 +157,10 @@ impl Mediawiki { } } pub fn get_csrf_token(&self) -> Result<(), Box> { - let url = - format!("{}/api.php?", self.server_url); - let params: Box<[(&str, &str)]> = Box::from([ - ("format", "json"), - ("meta", "tokens"), - ("action", "query") - ]); - let resp: String = self.make_request(url, params, ValidRequestTypes::Get)?; + let url = format!("{}/api.php?", self.server_url); + let params: Box<[(&str, &str)]> = + Box::from([("format", "json"), ("meta", "tokens"), ("action", "query")]); + let resp: String = self.make_request(url, params, ValidRequestTypes::Get)?; verboseln!("Raw response csrf: {}", resp); let response_deserialized: QueryResponseCsrf = serde_json::from_str(&resp)?; let token = response_deserialized.query.tokens.csrftoken; @@ -171,144 +173,209 @@ impl Mediawiki { Ok(()) } - pub fn make_request(&self, url: String, params: Box<[(&str, &str)]>, request_type: ValidRequestTypes) -> Result> { - let resp: Result> = match - match request_type { - ValidRequestTypes::Get => { - self - .client - .get(url) - //.basic_auth(&self.http_user, Some(&self.http_password)) // TODO: ZU TESTZWECKEN ENTFERNT - .query(¶ms) - .send() - } - ValidRequestTypes::Post | ValidRequestTypes::PostForEditing => { - // convert the params into a HashMap for JSON - let params_map: std::collections::HashMap<_, _> = params.iter().cloned().collect(); - self - .client - .post(url) - //.basic_auth(&self.http_user, Some(&self.http_password)) // TODO: ZU TESTZWECKEN ENTFERNT - .query(¶ms_map) - .send() - } - } - { + pub fn make_request( + &self, url: String, params: Box<[(&str, &str)]>, request_type: ValidRequestTypes, + ) -> Result> { + let resp: Result> = match match request_type { + ValidRequestTypes::Get => { + self.client + .get(url) + //.basic_auth(&self.http_user, Some(&self.http_password)) // TODO: ZU TESTZWECKEN ENTFERNT + .query(¶ms) + .send() + }, + ValidRequestTypes::Post | ValidRequestTypes::PostForEditing => { + // convert the params into a HashMap for JSON + let params_map: std::collections::HashMap<_, _> = params.iter().cloned().collect(); + self.client + .post(url) + //.basic_auth(&self.http_user, Some(&self.http_password)) // TODO: ZU TESTZWECKEN ENTFERNT + .form(¶ms_map) + .send() + }, + } { Ok(response) => { if response.status().is_success() { match request_type { ValidRequestTypes::PostForEditing => Ok(response.text()?), - _ => Ok(response.text()?) + _ => Ok(response.text()?), } + } else { + Err(format!( + "Failed to connect to wiki server: HTTP status code {}", + response.status() + ) + .into()) } - else { - Err(format!("Failed to connect to wiki server: HTTP status code {}", response.status()).into()) - } - } + }, Err(e) => { if e.is_connect() { Err(format!("Failed to connect to wiki server. Please check your internet connection or the server URL.\n(Error: {})", e).into()) } else { - Err(format!("An error occurred while sending the request to the wiki server: {}", e).into()) + Err(format!( + "An error occurred while sending the request to the wiki server: {}", + e + ) + .into()) } - } + }, }; resp } + /// Creates a completely new wiki page with page_content and page_title as inputs - pub fn new_wiki_page(&self, page_title: &str, page_content: &str, update_main_page: bool) -> Result> { + pub fn new_wiki_page( + &self, page_title: &str, page_content: &str, update_main_page: ValidPageEdits, + ) -> Result> { // Prevent dry run from making actual wiki edits if self.is_dry_run { - println!("Dry run: Would create wiki page '{}' with content {}", page_title, page_content); + println!( + "Dry run: Would create wiki page '{}' with content {}", + page_title, page_content + ); return Ok("Dry run - no actual page created".to_string()); } - + // Ensure we have a CSRF token if self.csrf_token.get().is_none() { return Err("CSRF token not set. Call get_csrf_token() first.".into()); } - + let url = format!("{}/api.php", self.server_url); - let params: Box<[(&str, &str)]> = Box::from([ - ("action", "edit"), - ("format", "json"), - ("title", page_title), - ("text", page_content), - ("token", self.csrf_token.get().unwrap()), - ("createonly", "true"), // Prevent overwriting existing pages - ("bot", "true"), - ]); - + + let params: Box<[(&str, &str)]> = match update_main_page { + ValidPageEdits::WithPotentiallyOverriding => { + // This means we *EDIT* the *Main Page* and do not prevent overwriting + Box::from([ + ("action", "edit"), + ("format", "json"), + ("title", page_title), + ("text", page_content), + ("token", self.csrf_token.get().unwrap()), + ("bot", "true") + ]) + } + ValidPageEdits::ModifyPlenumPageAfterwards_WithoutOverriding => { + // This means we *CREATE* a *new Page* and always prevent overwriting + Box::from([ + ("action", "edit"), + ("format", "json"), + ("title", page_title), + ("text", page_content), + ("token", self.csrf_token.get().unwrap()), + ("createonly", "true"), // Prevent overwriting existing pages + ("bot", "true"), + ]) + } + ValidPageEdits::WithoutOverriding => { + // This means we *CREATE* a *new Page* and always prevent overwriting + Box::from([ + ("action", "edit"), + ("format", "json"), + ("title", page_title), + ("text", page_content), + ("token", self.csrf_token.get().unwrap()), + ("createonly", "true"), // Prevent overwriting existing pages + ("bot", "true"), + ]) + } + }; + + verboseln!("Current page title: {page_title}"); // Log the request details for debugging verboseln!("Creating wiki page: {} at {}", page_title, url); - + // Make the request to create the page - let request_result = self.make_request(url, params, ValidRequestTypes::PostForEditing)?; - + let request_result: String = + self.make_request(url, params, ValidRequestTypes::PostForEditing)?; + verboseln!("pos1"); // Parse the response to check for success - let response: serde_json::Value = serde_json::from_str(&request_result)?; - + let response_result = serde_json::from_str::(&request_result); + let response = response_result.unwrap_or_else(|e| { + print!("Error while creating new wiki page:\n{}", e.to_string().cyan()); + return serde_json::from_str("\"(error)\"").unwrap() + }); + verboseln!("pos2"); // Check if the page creation was successful if let Some(edit) = response.get("edit") { + verboseln!("pos3"); if edit.get("result").and_then(|r| r.as_str()) == Some("Success") { verboseln!("Successfully created wiki page: {}", page_title); - - // Update the main plenum page if requested - if update_main_page { - self.update_plenum_page(page_title)?; - } - - Ok(request_result) + verboseln!("pos4"); } else { - Err(format!("Failed to create wiki page. Response: {}", response).into()) + print!("Failed to create wiki page. Response: {response}"); + verboseln!("pos5"); } } else { - Err(format!("Unexpected response when creating wiki page: {}", response).into()) - } + print!("Unexpected response when creating wiki page: {response}"); + verboseln!("pos6"); + }; + verboseln!("pos7"); + // Update the main plenum page if requested + match update_main_page { + ValidPageEdits::ModifyPlenumPageAfterwards_WithoutOverriding => { + verboseln!("updating main page..."); + self.update_plenum_page(page_title)? + }, + _ => () + }; + return Ok(page_title.to_string()) } /// This function is responsible for updating the main plenum page: /// /// It downloads the main plenum page from Mediawiki, inserts the /// new Link to the newly uploaded plenum pad and uploads the - /// page back to mediawiki. - pub fn update_plenum_page(&self, new_page_title_to_link_to: &str) -> Result<(), Box> { + /// page back to mediawiki. + pub fn update_plenum_page( + &self, new_page_title_to_link_to: &str, + ) -> Result<(), Box> { let current_year = Utc::now().year().to_string(); let year_heading_pattern = format!("=== {} ===", current_year); - + // Download Plenum page content let mut page_content = self.get_page_content(&self.plenum_main_page_name)?; - - // check if the current year heading pattern exists - if !page_content.contains(&year_heading_pattern) { - // If not, add a new year heading pattern - let last_year = (current_year.parse::()? - 1).to_string(); - let last_year_heading_pattern = format!("=== {} ===", last_year); - - if page_content.contains(&last_year_heading_pattern) { - // add a new year heading pattern before the last year one - let parts: Vec<&str> = page_content.split(&last_year_heading_pattern).collect(); - page_content = format!( - "{}=== {} ===\n* [[{}]]\n\n{}{}", - parts[0], current_year, new_page_title_to_link_to, - last_year_heading_pattern, - parts[1] - ); + + // check first if the script has been run before and if the link already exists + if !page_content.contains(&new_page_title_to_link_to) { + + // check if the current year heading pattern exists + if !page_content.contains(&year_heading_pattern) { + // If not, add a new year heading pattern + let last_year = (current_year.parse::()? - 1).to_string(); + let last_year_heading_pattern = format!("=== {} ===", last_year); + + if page_content.contains(&last_year_heading_pattern) { + // add a new year heading pattern before the last year one + let parts: Vec<&str> = page_content.split(&last_year_heading_pattern).collect(); + page_content = format!( + "{}=== {} ===\n* [[{}]]\n\n{}{}", + parts[0], + current_year, + new_page_title_to_link_to, + last_year_heading_pattern, + parts[1] + ); + } else { + // Fallback: add the current year heading to the end of the page + page_content.push_str(&format!( + "\n\n=== {} ===\n* [[{}]]", + current_year, new_page_title_to_link_to + )); + } } else { - // Fallback: add the current year heading to the end of the page - page_content.push_str(&format!("\n\n=== {} ===\n* [[{}]]", current_year, new_page_title_to_link_to)); + // Paste the link below the current year heading + page_content = page_content.replace( + &year_heading_pattern, + &format!("{}\n* [[{}]]", year_heading_pattern, new_page_title_to_link_to), + ); } } else { - // Paste the link below the current year heading - page_content = page_content.replace( - &year_heading_pattern, - &format!("{}\n* [[{}]]", year_heading_pattern, new_page_title_to_link_to) - ); + verboseln!("{}", "The bot appears to have been run before and a duplicate link to the new plenum pad was avoided.".yellow()) } - // refresh page - self.new_wiki_page(&self.plenum_main_page_name, &page_content, false)?; + self.new_wiki_page(&self.plenum_main_page_name, &page_content, ValidPageEdits::WithPotentiallyOverriding)?; Ok(()) } /// This function downloads and returns the contents of a wiki page when given the page's title (e.g. `page_title = Plenum/13._August_2024`) @@ -323,33 +390,31 @@ impl Mediawiki { ]); let resp = self.make_request(url, params, ValidRequestTypes::Get)?; let response_deserialized: serde_json::Value = serde_json::from_str(&resp)?; - + let wikitext = response_deserialized .get("parse") .and_then(|parse| parse.get("wikitext")) .and_then(|text| text.as_str()) .ok_or("Expected field `wikitext` not found")?; - + Ok(wikitext.to_string()) } pub fn test_wiki_write(&self) -> Result<(), Box> { // Generate a unique test page title - let test_page_title = format!( - "TestPage/WikiWriteTest-{}", - chrono::Utc::now().format("%Y%m%d%H%M%S") - ); - + let test_page_title = + format!("TestPage/WikiWriteTest-{}", chrono::Utc::now().format("%Y%m%d%H%M%S")); + // Test content to write let test_content = format!( "Wiki Write Test\n\nThis is a test page generated at {}. \nIt can be safely deleted.", chrono::Utc::now() ); - + // Ensure login token and CSRF token are set let _login_result = self.login()?; self.get_csrf_token()?; - + let url = format!("{}/api.php", self.server_url); let params: Box<[(&str, &str)]> = Box::from([ ("action", "edit"), @@ -360,25 +425,26 @@ impl Mediawiki { ("createonly", "true"), ("bot", "true"), ]); - + // Manually print out all parameters for debugging println!("Debug - URL: {}", url); println!("Debug - Parameters:"); for (key, value) in params.iter() { println!(" {}: {}", key, value); } - + // Make request and capture the full response - let request_result = match self.client + let request_result = match self + .client .post(&url) .basic_auth(&self.http_user, Some(&self.http_password)) .form(¶ms) - .send() + .send() { Ok(response) => { println!("Debug - Response Status: {}", response.status()); println!("Debug - Response Headers: {:?}", response.headers()); - + match response.text() { Ok(text) => { println!("Debug - Raw Response Body:\n{}", text); @@ -386,19 +452,19 @@ impl Mediawiki { }, Err(e) => { return Err(format!("Failed to read response body: {}", e).into()); - } + }, } }, Err(e) => { return Err(format!("Request failed: {}", e).into()); - } + }, }; - + // Attempt to parse the response match serde_json::from_str::(&request_result) { Ok(response) => { println!("Debug - Parsed Response: {}", response); - + if let Some(edit) = response.get("edit") { if edit.get("result").and_then(|r| r.as_str()) == Some("Success") { println!("✅ Successfully created test page: {}", test_page_title); @@ -409,10 +475,11 @@ impl Mediawiki { } else { Err(format!("Unexpected response: {}", response).into()) } - } + }, Err(e) => { - Err(format!("JSON parsing failed. Error: {}. Raw response: {}", e, request_result).into()) - } + Err(format!("JSON parsing failed. Error: {}. Raw response: {}", e, request_result) + .into()) + }, } } } @@ -421,29 +488,31 @@ impl Mediawiki { /// logging in to mediawiki, retrieving the necessary tokens, creating a /// new wiki page for the current plenum protocol, and for linking the new /// page to the main plenum overview page. -pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki, plenum_date: &NaiveDate) -> Result<(), Box> { +pub fn pad_ins_wiki( + old_pad_content: String, wiki: &Mediawiki, plenum_date: &NaiveDate, +) -> Result<(), Box> { // Use the provided date or default to current date let date = plenum_date; - wiki.test_wiki_write()?; + // wiki.test_wiki_write()?; // Login to Wiki and get required tokens for logging in and writing verboseln!("logging in..."); let login_result = wiki.login()?; verboseln!("Login done."); trace_var!(login_result); - + wiki.get_csrf_token()?; verboseln!("CSRF token acquired."); // Convert to mediawiki and make new page let pad_converted = pandoc_convert(old_pad_content)?; trace_var!(pad_converted); - + // Create a new wiki page plenum_main_page_name/page_title, e.g. under Plenum/13._August_2024 verboseln!("wiki: uploading converted pad"); let page_title = create_page_title(date); let full_page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title); - - wiki.new_wiki_page(&full_page_title, &pad_converted, true)?; + + wiki.new_wiki_page(&full_page_title, &pad_converted, ValidPageEdits::ModifyPlenumPageAfterwards_WithoutOverriding)?; verboseln!("Finished successfully with wiki"); Ok(())