#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:
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
}
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;
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_tree_html (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);
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 - the index file for this tree and all the documents and
- // the child trees recursively
- void DocumentTree::create_tree_html (std::string destdir) {
+ // 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
// 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());
- SideBarItem item0;
- item0.set_sidebar_text (INDEX);
- item0.set_sidebar_url (urlpath + "index.html");
+ 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) {
- SideBarItem item1;
- item1.set_sidebar_text (GO_UP);
- item1.set_sidebar_url (this->stub_hierarchy() + "index.html");
+ 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);
}
- index.get()->add_side_bar (*bar1.get());
-
+
// create a sidebar for the child levels if there are children
std::unique_ptr<SideBar> bar2 (new SideBar ());
- bar2.get()->set_title (SUB_CAT + this->title);
+ 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");
bar2.get()->add_sidebar_item (item);
}
- index.get()->add_side_bar (*bar2.get());
// create the path and then the index file
std::filesystem::create_directories (filepath);
// Create the list of documents in this tree with links
- // Reuse the sidebar class and sidebaritem class which is
- // basically a list of links but instead of adding a sidebar
- // add it to the content portion
- SideBar article_list;
- article_list.set_title (this->title + ": " + ART_LIST);
+ 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) {
- SideBarItem item;
- item.set_sidebar_text (doc.get_title());
- item.set_sidebar_url (urlpath + doc.get_filename() + ".html");
- article_list.add_sidebar_item (item);
- // output the document also, add the side bars
- if (this->get_parent() != nullptr)
- doc.add_side_bar (*bar1.get());
+ // 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 (filepath);
+ doc.output_to_html (tpl, filepath);
}
- index.get()->set_content (article_list.to_html());
- index.get()->output_to_html (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);
- // create index for children
+ // recursively create index for children
for (DocumentTree tree : this->children)
- tree.create_tree_html (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);
}
}