From 5fc2717c7410ac873761b86200c8da73c0a4c62c Mon Sep 17 00:00:00 2001
From: Pierre Jarriges <pierre.jarriges@tutanota.com>
Date: Tue, 4 Oct 2022 11:00:18 +0200
Subject: [PATCH] remove template

---
 src/main.rs            |   3 +-
 src/service/website.rs |  25 +++++++
 src/website/website.rs | 151 ++++++++++++++++++++++++++---------------
 3 files changed, 122 insertions(+), 57 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index b590417..66b5818 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -66,7 +66,8 @@ async fn main() -> std::io::Result<()> {
                             .service(service::files::delete_static_file)
                             .service(service::add_template)
                             .service(service::update_template)
-                            .service(service::remove_page),
+                            .service(service::remove_page)
+                            .service(service::remove_template),
                     ),
             )
             .service(service::files::favicon)
diff --git a/src/service/website.rs b/src/service/website.rs
index 186761c..139cf98 100644
--- a/src/service/website.rs
+++ b/src/service/website.rs
@@ -170,6 +170,7 @@ pub async fn remove_page(
     let mut website = website
         .write()
         .expect("Couldn't acquire website write lock");
+
     match website.remove_page(id.into_inner()) {
         Ok(()) => {
             website
@@ -180,3 +181,27 @@ pub async fn remove_page(
         Err(msg) => HttpResponse::BadRequest().body(msg),
     }
 }
+
+#[delete("remove-template/{name}")]
+pub async fn remove_template(
+    name: actix_web::web::Path<String>,
+    app_state: Data<RwLock<AppState>>,
+    website: Data<RwLock<WebSite>>,
+) -> HttpResponse {
+    let mut website = website
+        .write()
+        .expect("Couldn't acquire website write lock");
+
+    match website.remove_template(name.into_inner()) {
+        Ok(()) => {
+            if let Err(msg) =
+                website.save(&app_state.read().expect("Couldn't read AppState").config)
+            {
+                return HttpResponse::InternalServerError()
+                    .body(format!("Error saving website to static file {}", msg));
+            }
+            HttpResponse::Accepted().finish()
+        }
+        Err(msg) => HttpResponse::BadRequest().body(msg),
+    }
+}
diff --git a/src/website/website.rs b/src/website/website.rs
index 229850e..e5257e1 100644
--- a/src/website/website.rs
+++ b/src/website/website.rs
@@ -194,14 +194,7 @@ impl WebSite {
         page: &'a Page,
         parent_id: Option<usize>,
         match_id: usize,
-    ) -> Result<usize, String> {
-        let parent_id = {
-            if parent_id.is_none() {
-                return Err("Page must have a parent".to_string());
-            }
-            parent_id.unwrap()
-        };
-
+    ) -> Result<Option<usize>, String> {
         if page.get_id() == match_id {
             return Ok(parent_id);
         }
@@ -223,7 +216,7 @@ impl WebSite {
         Self::find_page_mut(&mut self.root_page, id)
     }
 
-    fn get_page_parent_id(&self, id: usize) -> Result<usize, String> {
+    fn get_page_parent_id(&self, id: usize) -> Result<Option<usize>, String> {
         Self::find_page_parent_id(&self.root_page, None, id)
     }
 
@@ -295,12 +288,14 @@ impl WebSite {
         Ok(())
     }
 
-    fn get_template(&self, name: &String) -> Option<&PageTemplate> {
-        self.templates.iter().find(|t| t.name.eq(name))
+    fn get_template<S: Into<String>>(&self, name: S) -> Option<&PageTemplate> {
+        let name = name.into();
+        self.templates.iter().find(|t| t.name.eq(&name))
     }
 
-    fn get_template_mut(&mut self, name: &String) -> Option<&mut PageTemplate> {
-        self.templates.iter_mut().find(|t| t.name.eq(name))
+    fn get_template_mut<S: Into<String>>(&mut self, name: S) -> Option<&mut PageTemplate> {
+        let name = name.into();
+        self.templates.iter_mut().find(|t| t.name.eq(&name))
     }
 
     pub fn add_template(&mut self, new_template: PageTemplate) -> Result<&PageTemplate, String> {
@@ -329,6 +324,36 @@ impl WebSite {
         }
     }
 
+    fn validate_remove_template(&self, name: &String) -> Result<(), String> {
+        for p in self.get_all_pages_as_vec() {
+            if p.template_name.eq(name) {
+                return Err(format!(
+                    "Template \"{}\" is used by page \"{}\" and can't be removed",
+                    name, p.metadata.url_slug
+                ));
+            }
+        }
+
+        Ok(())
+    }
+
+    pub fn remove_template<S: Into<String>>(&mut self, name: S) -> Result<(), String> {
+        let name: String = name.into();
+
+        if let Err(msg) = self.validate_remove_template(&name) {
+            return Err(msg);
+        }
+
+        self.templates = self
+            .templates
+            .iter()
+            .filter(|t| !t.name.eq(&name))
+            .map(|t| t.clone())
+            .collect::<Vec<PageTemplate>>();
+
+        Ok(())
+    }
+
     pub fn update_page_rec_after_template_update(page: &mut Page, template: &PageTemplate) {
         page.update_template_if_same_name(template);
         for sp in page.sub_pages.iter_mut() {
@@ -337,12 +362,13 @@ impl WebSite {
     }
 
     fn validate_page_to_remove(&mut self, id: usize) -> Result<usize, String> {
-        let res = self.get_page_parent_id(id);
-        if let Err(msg) = res {
-            return Err(msg);
+        match self.get_page_parent_id(id) {
+            Ok(parent_id) => match parent_id {
+                Some(parent_id) => Ok(parent_id),
+                None => Err("Page must have a parent".to_string()),
+            },
+            Err(msg) => Err(msg),
         }
-
-        Ok(res.unwrap())
     }
 
     pub fn remove_page(&mut self, id: usize) -> Result<(), String> {
@@ -365,7 +391,39 @@ impl WebSite {
 #[cfg(test)]
 mod test_website {
     use super::*;
-
+    const TEST_TEMPLATE: &'static str = "
+{
+    \"name\": \"TEST TEMPLATE 2\",
+    \"layout\": {},
+    \"contents\": [
+        {
+            \"tag\": \"div\",
+            \"attrs\": {
+                \"class\": \"page-template\"
+            },
+            \"contents\": [
+                {
+                    \"tag\": \"header\",
+                    \"contents\": [
+                        {
+                            \"tag\": \"nav\"
+                        }
+                    ]
+                },
+                {
+                    \"tag\": \"div\",
+                    \"attrs\": {
+                        \"id\": \"page-body\"
+                    }
+                },
+                {
+                    \"tag\": \"footer\"
+                }
+            ]
+        }
+    ]
+}
+        ";
     fn test_website(pth: &PathBuf) -> WebSite {
         WebSiteBuilder::testing(pth).build().unwrap()
     }
@@ -454,50 +512,16 @@ mod test_website {
 
     #[test]
     fn add_template() {
-        let test_template: &'static str = "
-{
-    \"name\": \"TEST ADD TEMPLATE\",
-    \"layout\": {},
-    \"contents\": [
-        {
-            \"tag\": \"div\",
-            \"attrs\": {
-                \"class\": \"page-template\"
-            },
-            \"contents\": [
-                {
-                    \"tag\": \"header\",
-                    \"contents\": [
-                        {
-                            \"tag\": \"nav\"
-                        }
-                    ]
-                },
-                {
-                    \"tag\": \"div\",
-                    \"attrs\": {
-                        \"id\": \"page-body\"
-                    }
-                },
-                {
-                    \"tag\": \"footer\"
-                }
-            ]
-        }
-    ]
-}
-        ";
-
         let test_dir = PathBuf::from("./test");
         let mut ws = test_website(&test_dir);
 
-        let new_template: PageTemplate = serde_json::from_str(test_template).unwrap();
+        let new_template: PageTemplate = serde_json::from_str(TEST_TEMPLATE).unwrap();
 
         let add_template_res = ws.add_template(new_template);
         assert!(add_template_res.is_ok());
 
         let added_template = add_template_res.unwrap();
-        assert_eq!(added_template.name, "TEST ADD TEMPLATE");
+        assert_eq!(added_template.name, "TEST TEMPLATE 2");
 
         remove_test_dir(&test_dir);
     }
@@ -532,10 +556,25 @@ mod test_website {
         let test_dir = PathBuf::from("./test");
         let mut ws = test_website(&test_dir);
         let remove_root_page = ws.remove_page(1);
-        assert!(remove_root_page.is_err());
+        assert!(remove_root_page.is_err()); // Shouldn't be able to remove root page
         let remove_subpage = ws.remove_page(2);
         assert!(remove_subpage.is_ok());
         assert!(ws.get_page(2).is_none());
         remove_test_dir(&test_dir);
     }
+
+    #[test]
+    fn test_remove_template() {
+        let test_dir = PathBuf::from("./test");
+        let mut ws = test_website(&test_dir);
+        let mut res = ws.remove_template("TEST TEMPLATE");
+        assert!(res.is_err()); // Template shouldn't be removable if it's used
+
+        ws.add_template(serde_json::from_str(TEST_TEMPLATE).unwrap())
+            .unwrap();
+        res = ws.remove_template("TEST TEMPLATE 2");
+        assert!(res.is_ok());
+        assert!(ws.get_template("TEST TEMPLATE 2").is_none());
+        remove_test_dir(&test_dir);
+    }
 }
-- 
GitLab