diff --git a/src/main.rs b/src/main.rs index a67e886..e1bd3a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -94,6 +94,8 @@ 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), diff --git a/src/mediawiki.rs b/src/mediawiki.rs index 420bef0..fc837d8 100644 --- a/src/mediawiki.rs +++ b/src/mediawiki.rs @@ -1,10 +1,10 @@ use std::cell::OnceCell; use std::error::Error; +use chrono::{Datelike, Utc}; use colored::Colorize; use reqwest::blocking::Client; use serde::Deserialize; -use serde_json::json; use crate::{trace_var, verboseln}; use crate::config_spec::{CfgField, CfgGroup}; @@ -149,11 +149,13 @@ impl Mediawiki { .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)) ZU TESTZWECKEN ENTFERNT - .form(¶ms) + .json(¶ms_map) .send() } } @@ -179,8 +181,9 @@ impl Mediawiki { }; 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) -> Result> { + pub fn new_wiki_page (&self, page_title: &str, page_content: &str, update_main_page: bool) -> Result> { // action=edit&format=json&title=Wikipedia:Sandbox&appendtext=Hello&token=sampleCsrfToken123+\ let url = format!("{}/api.php?", self.server_url); @@ -192,28 +195,72 @@ impl Mediawiki { ("token", self.csrf_token.get().unwrap()), // A "csrf" token retrieved from action=query&meta=tokens ("bot", "true"), // Mark this edit as a bot edit. ]); + + println!("Making request to {} with params: {:?}", url, params); + let request_result = self.make_request(url, params, ValidRequestTypes::Post); - - self.update_plenum_page(page_title)?; - + + if update_main_page { + self.update_plenum_page(page_title)?; + } request_result } - /// Downloads the main Plenum Page from Mediawiki, inserts the Link to the new Page and replaces the content of the mediawiki- + /// 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> { - // 1. Download Plenum page content + // 1. Get the current year: + let current_year = Utc::now().year().to_string(); + let year_pattern = format!("=== {} ===", current_year); + + // 2. Download Plenum page content let page_content = self.get_page_content(&self.plenum_main_page_name)?; - trace_var!(page_content); - let current_year = "2024"; // TODO: Datumslogik einbauen - let year_section = format!("=== {} ===\n", current_year); - if page_content.contains(&year_section) { - let mut content_split: Vec<&str> = page_content.split(&year_section).collect(); - println!("Length: {}", content_split.len()); - let rest_of_content = content_split.pop().unwrap_or_default(); - let updated_section = format!("{}{}\n* {}", content_split.join(&year_section), year_section, new_page_title_to_link_to); - //format!("{}{}", updated_section, rest_of_content) + //trace_var!(page_content); + + // 3. Check if the current year exists + let mut updated_section = String::new(); + if page_content.contains(&year_pattern) { + // 4. Year Heading exists, inserting new link below + let mut content_split: Vec<&str> = page_content.split(&year_pattern).collect(); + let site_content_below_year_heading = content_split.pop().unwrap_or_default(); + let site_content_above_year_heading = content_split.pop().unwrap_or_default(); + updated_section = format!( + "{}{}\n* [[{}]]{}", + site_content_above_year_heading, + year_pattern, + new_page_title_to_link_to, + site_content_below_year_heading); + verboseln!("updated page: {}", updated_section) + } else { + // Scenario: New year: Generating new heading for the current year + // 4. First checking if the last year exists: + let last_year = current_year.parse::()? - 1; // returns e.g. 2023 when given 2024 + let last_year_pattern = format!("=== {} ===", last_year); + if page_content.contains(&last_year_pattern) { + // 2. Secondly, generate a new heading for this year above last year's + verboseln!("new year, adding this year's heading"); + let mut content_split: Vec<&str> = page_content.split(&last_year_pattern).collect(); + let site_content_below_year_heading = content_split.pop().unwrap_or_default(); + let site_content_above_year_heading = content_split.pop().unwrap_or_default(); + updated_section = format!( + "{}{}\n* [[{}]]\n\n{}{}", + site_content_above_year_heading, + year_pattern, + new_page_title_to_link_to, + last_year_pattern, + site_content_below_year_heading); + } else { + println!("There is neither a {}, nor a {} heading on the main plenum Page!", current_year, last_year); + panic!("The Plenum Main Page seems seriously damaged, aborting...") + } } + // uploading the pad again + self.new_wiki_page(&self.plenum_main_page_name, &updated_section, false)?; // nicht auf true setzen, sonst entsteht eine Endlosschleife 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`) pub fn get_page_content (&self, page_title: &str) -> Result> { let url = format!("{}/api.php?", self.server_url); @@ -225,41 +272,20 @@ impl Mediawiki { ("formatversion", "2"), ]); let resp = self.make_request(url, params, ValidRequestTypes::Get)?; - let resp = json!(resp); - Ok(resp["parse"]["wikitext"].to_string()) - } - pub fn get_page_section_title (&self, page_title: &str, section_number: &str) -> Result> { - let url = - format!("{}/api.php?", self.server_url); - let params: Box<[(&str, &str)]> = Box::from([ - ("action", "parse"), // Create and edit pages. - ("contentmodel", "wikitext"), - ("format", "json"), - ("page", page_title), - ]); - let resp = self.make_request(url, params, ValidRequestTypes::Get)?; - todo!() - //let response_deserialized = serde_json::from_str(&resp)?; - //Ok(response_deserialized["parse"]) - } - pub fn edit_section (&self, page_title: &str, text_to_prepend: &str, section_number: &str) -> Result> { - let url = - format!("{}/api.php?", self.server_url); - let params: Box<[(&str, &str)]> = Box::from([ - ("action", "edit"), // Create and edit pages. - ("format", "json"), - ("title", page_title), // Title of the page to edit. Cannot be used together with pageid. - ("section", section_number), // Section identifier. 0 for the top section, new for a new section. Often a positive integer, but can also be non-numeric - ("prependtext", text_to_prepend), // Add this text to the end of the page or section. Overrides text. - ("token", self.csrf_token.get().unwrap()), // A "csrf" token retrieved from action=query&meta=tokens - ("bot", "true"), // Mark this edit as a bot edit. - ]); - let request_result = self.make_request(url, params, ValidRequestTypes::Post); + let response_deserialized: serde_json::Value = serde_json::from_str(&resp)?; - request_result + let resp = response_deserialized + .get("parse") + .and_then(|parse | parse.get("wikitext")) + .ok_or("Expected field `wikitext` not found, likely no access to main plenum page")?; + Ok(resp.to_string()) } } +/// This function runs at the end of do_protocol() and is responsible for +/// 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) -> Result<(), Box> { // Login to Wiki and get required tokens for logging in and writing wiki.get_login_token()?; @@ -273,14 +299,25 @@ pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki) -> Result<(), Box // Convert to mediawiki and make new page let pad_converted = pandoc_convert(old_pad_content)?; trace_var!(pad_converted); - let page_title = "Page Test 5"; - let page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title); // Example: Plenum/13._August_2024 - wiki.new_wiki_page(&page_title, &pad_converted)?; + + // Create a new wiki page plenum_main_page_name/page_title, e.g. under Plenum/13._August_2024 + // and past the converted pad there + verboseln!("wiki: uploading converted pad"); + // let page_title = "14._August_2024"; + let page_title = "14._AUGUST_2024"; + let page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title); + wiki.new_wiki_page(&page_title, &pad_converted, true)?; + // Download the main plenum page and insert the new page_title as a link + verboseln!("wiki: updating main plenum page"); + + wiki.update_plenum_page(&page_title)?; + verboseln!("Finished successfully with wiki"); Ok(()) } -/// Takes a String in the Markdown format and returns a String in the mediawiki Format +/// Takes a String in the Markdown format, converts it and returns it in +/// the Mediawiki format fn pandoc_convert(markdown: String) -> Result> { let (output, errors, status) = crate::pipe( "pandoc", @@ -288,6 +325,7 @@ fn pandoc_convert(markdown: String) -> Result> { markdown, )?; if status.success() { + println!("Resultat von Pandoc: {}", output); Ok(output) } else { Err(format!("Pandoc error, exit code {:?}\n{}", status, errors).into()) @@ -302,8 +340,10 @@ fn create_title (nächster_plenumstermin: String) { */ -// This has to be defined that way, because both in the login and csrf token, -// the response contains two \\ characters which break the usual deserialization +/// Deserialization must be done this way because the response contains +/// two `\\` characters in both the login and csrf tokens, which breaks the +/// usual deserialization + #[derive(Deserialize)] struct QueryResponseLogin { #[allow(dead_code)]