Readme.md - added Section for customization and notes
[biaweb2.git] / biawebdocumenttree.hpp
index 41aa81a..a9f1ccd 100644 (file)
@@ -2,12 +2,19 @@
 #define __BIAWEBDOCUMENTTREE__
 #include <memory>
 #include <list>
+#include <array>
 #include <iostream>
+#include <fstream>
 #include <filesystem>
+#include <sys/stat.h>
 #include "biawebdocument.hpp"
 #include "biawebstrings.hpp"
+#include "biawebutil.hpp"
+#include "biawebdoclist.hpp"
+#include "biawebtemplate.hpp"
+#include "biawebrss.hpp"
 
-// to implement a document tree - both with or without subtrees
+// class to implement a document tree - both with or without subtrees
 namespace biaweb {
     class DocumentTree {
       protected:
@@ -17,6 +24,9 @@ namespace biaweb {
         std::list<DocumentTree> children;
         // title of this tree
         std::string title;
+        // summary for this tree - this is displayed in the index.html file of
+        // this tree before the list of articles in the tree
+        std::string summary;
         // file stub of this tree
         std::string stub;
         // list of documents in this tree
@@ -28,9 +38,12 @@ namespace biaweb {
         }
 
       public:
+        // method to build a document tree from a path
+        void document_tree_builder (std::string srcpath);
+
         // create new top level document tree
         DocumentTree (std::string title, std::string stub = "") {
-            this->title = title;
+            this->title = escape_html (title);
             // if stub is not empty set it
             if (stub != "")
                 this->stub = stub;
@@ -40,12 +53,32 @@ namespace biaweb {
             this->parent = nullptr;
         }
 
+        // set the summary for this tree
+        void set_summary (std::string summary) {
+            this->summary = summary;
+        }
+
+        // set the summary for this tree as markdown text 
+        void set_markdown_summary (std::string summary) {
+            this->summary = convert_to_markdown (summary);
+        }
+
+        std::string get_summary () {
+            return this->summary;
+        }
+
+        // sort the documents as per creation time from latest to oldest
+        void sort_documents_creation_time () {
+            this->docs.sort ([] (Document &a, Document &b) 
+                    {return (a.get_creation_date() > b.get_creation_date()); });
+        }
+
         // create the document index for this tree
-        void create_index (std::string destdir);
+        void create_tree_html (Template *t, std::string destdir);
 
         // set the title 
         void set_title (std::string title) {
-            this->title = title;
+            this->title = escape_html (title);
             // if no stub is set
             if (this->stub == "")
                 this->stub = convert_title (title);
@@ -125,14 +158,17 @@ namespace biaweb {
             for (unsigned int i = 0; i < this->get_level(); i ++)
                 std::cout <<  "+--";
             // print the title of this tree
-            std::cout << this->title << std::endl;
+            std::cout << this->title << std::endl;            
             // recurse through the child trees if any and so on
             for (DocumentTree child : children)
                 child.visualize_tree ();
     }
 
-    // create the tree index - the index file for this tree
-    void DocumentTree::create_index (std::string destdir) {
+    // create the tree - the index file for this tree and all the documents and
+    // the child trees recursively - using the template specified
+    void DocumentTree::create_tree_html (Template *tpl, std::string destdir) {
+
+        // create a document to represent the index of the tree.
         std::unique_ptr<Document> index (new Document (this->title));
         index.get()->set_index ();
         // set the file name path
@@ -145,26 +181,169 @@ namespace biaweb {
         if (urlpath != "")
             urlpath += "/";
 
-        // create the sidebar
-        std::unique_ptr<SideBar> bar (new SideBar ());
-        bar.get()->set_title (SUB_CAT + this->title);
+        // create the sidebars
+        // First sidebar
+        // Create a link to the index page and 
+        // If this tree has a parent, create a sidebar link to the level up
+        std::unique_ptr<SideBar> bar1 (new SideBar());
+        GenericLinkItem item0;
+        bar1.get()->set_title (tpl->get_stringbit (NAVIGATION));
+        item0.set_item_text (tpl->get_stringbit (INDEX));
+        item0.set_item_url (urlpath + "index.html");
+        bar1.get()->add_sidebar_item (item0);
+        if (this->get_parent() != nullptr) {
+            GenericLinkItem item1;
+            item1.set_item_text (tpl->get_stringbit(GO_UP));
+            item1.set_item_url (this->stub_hierarchy() + "index.html");
+            bar1.get()->add_sidebar_item (item1);
+        }
+        // create a sidebar for the child levels if there are children
+        std::unique_ptr<SideBar> bar2 (new SideBar ());
+        bar2.get()->set_title (tpl->get_stringbit (SUB_CAT) + this->title);
         for (DocumentTree tree : this->children) {
             // we use site relative URLs that rely on the base href tag
             // so for biaweb generated sites, the base href tag should be 
             // used in the main template
-            SideBarItem item (tree.get_title(), urlpath + 
+            GenericLinkItem item (tree.get_title(), urlpath + 
                                             tree.stub + "/" + "index.html");
-            bar.get()->add_sidebar_item (item);
+            bar2.get()->add_sidebar_item (item);
         }
-        index.get()->add_side_bar (*bar.get());
 
         // create the path and then the index file
         std::filesystem::create_directories (filepath);
-        index->output_to_html (filepath);
 
-        // create index for children
+        // Create the list of documents in this tree with links
+        std::unique_ptr<DocList> article_list (new DocList ());
+        article_list.get()->set_title (this->title + ": " + tpl->get_stringbit (ARTICLES_LIST));
+        // sort the documents as per creation time and then add the document
+        // links - newest documents should appear above older ones.
+        sort_documents_creation_time ();
+
+        // create the navigation bit 
+        std::shared_ptr<NavigationBit> navbit (new NavigationBit ());
+        auto par1 = this;
+        // get the link to each level in the hierarchy and add it as 
+        // an inline list
+        while (par1 != nullptr) {
+            if (par1->parent != nullptr)
+                navbit.get()->add_link_item (GenericLinkItem(par1->title, 
+                            par1->stub_hierarchy() + par1->stub + "/index.html"));
+            else
+                navbit.get()->add_link_item (GenericLinkItem(tpl->get_stringbit(HOME), "index.html"));
+            par1 = par1->parent;
+        }
+
+        // rss feed
+        std::unique_ptr<RSSFeed> feed (new RSSFeed ());
+        feed.get()->set_pub_date (index.get()->get_creation_date());
+        feed.get()->set_title (this->get_title());
+
+        // iterate through the documents and generate the document
+        for (Document doc : this->docs) {
+            // Add the document to RSS feed
+            RSSFeedItem fitem (doc.get_title(), doc.get_meta_desc (),
+                        urlpath + doc.get_filename() + ".html", 
+                        doc.get_creation_date());
+            // If the items don't exceed max size of RSS feed
+            if (feed.get()->get_num_items() < MAX_RSS_FEED)
+                feed.get()->add_rss_item (fitem);                        
+            // Add the document details to the document list
+            DocListItem item (&doc, urlpath);
+            article_list.get()->add_document_item (item);
+            // output the document also, add the navigation bit and side bars
+
+            doc.set_navigation_bit (*navbit.get());
+            doc.add_side_bar (*bar1.get());
+            doc.add_side_bar (*bar2.get());
+            doc.output_to_html (tpl, filepath);
+        }
+
+        // output the rss feed 
+        feed.get()->output_to_html (tpl, filepath);
+
+        // add the side bars
+        index.get()->add_side_bar (*bar1.get());
+        index.get()->add_side_bar (*bar2.get());
+
+        // sidebar for RSS feed
+        // if there are are items in the feed add the link
+        if (feed.get()->get_num_items () > 0) {
+            std::unique_ptr<SideBar> bar3 (new SideBar ());
+            bar3.get()->set_title (tpl->get_stringbit (SUBSCRIBE));
+            bar3.get()->add_sidebar_item (
+                    GenericLinkItem (tpl->get_stringbit(RSS_FEED), urlpath + "feed.xml"));
+            index.get()->add_side_bar (*bar3.get());
+        }
+
+        // add the navigation bit
+        index.get()->set_navigation_bit (*navbit.get());
+        // index should contain the summary followed by the the article list
+        // and the sub categories
+        index.get()->set_content (this->summary +
+                                article_list.get()->to_html(tpl) +
+                                bar2.get()->to_html (tpl));
+
+        // output the index file
+        index.get()->output_to_html (tpl, filepath);
+
+        // recursively create index for children
         for (DocumentTree tree : this->children)
-            tree.create_index (destdir);
+            tree.create_tree_html (tpl, destdir);
+    }
+
+    // build a document tree from a filesystem path recursively
+    void DocumentTree::document_tree_builder (std::string srcpath_str) {
+        std::filesystem::path srcpath (srcpath_str);
+        this->title = srcpath.stem().string ();
+
+        // Get the directories to this child and add them as sub document
+        // trees
+        try {
+            for (auto fsitem : std::filesystem::directory_iterator (srcpath) )
+            {
+                // if it is a directory then build the tree for that directory
+                if (fsitem.is_directory ()) {
+                    std::shared_ptr <DocumentTree> doctree 
+                                    (new DocumentTree (fsitem.path().filename().string()));
+                    
+                    this->add_child (doctree.get());
+                }
+                // add the regular files as documents in the tree and not symlink
+                else if (fsitem.is_regular_file () && !fsitem.is_symlink()) {
+                    // if it is an index file (specially named as index 
+                    // or index.md or whatever) directly add 
+                    // the contents to the summary of the Doctree 
+                    if (fsitem.path().stem().string() == "index")
+                    {
+                        std::string infilestr = load_from_file (fsitem.path ()); 
+                        this->set_markdown_summary (infilestr);
+                    }
+                    // else it is a non-index file-  
+                    // create a Document and add it to the tree
+                    else {
+                        std::ifstream infile (fsitem.path ());
+                        std::shared_ptr<Document> doc 
+                                (new Document (infile));
+                        infile.close ();
+                        
+                        // file modified date from system
+                        struct stat buf;
+                        if (stat (fsitem.path().string().c_str(), &buf) == 0)
+                            doc.get()->set_modified_date (buf.st_mtim.tv_sec);
+
+                        this->add_document (doc.get());
+                    }
+                }
+            }
+        }
+        catch (std::filesystem::filesystem_error) {
+            std::cout << NO_SUCH_PATH_ERROR << std::endl;
+        }
+
+        // add the trees for the children recursively
+        for (DocumentTree &child : this->children)
+            child.document_tree_builder (srcpath_str + "/" + child.title);
     }
 }