Document tree generation to HTML output completed
[biaweb2.git] / biawebdocumenttree.hpp
1 #ifndef __BIAWEBDOCUMENTTREE__
2 #define __BIAWEBDOCUMENTTREE__
3 #include <memory>
4 #include <list>
5 #include <iostream>
6 #include <filesystem>
7 #include "biawebdocument.hpp"
8 #include "biawebstrings.hpp"
9
10 // to implement a document tree - both with or without subtrees
11 namespace biaweb {
12 class DocumentTree {
13 protected:
14 // the pointer to the parent tree if there is one or nullptr
15 DocumentTree* parent;
16 // child trees
17 std::list<DocumentTree> children;
18 // title of this tree
19 std::string title;
20 // file stub of this tree
21 std::string stub;
22 // list of documents in this tree
23 std::list<Document> docs;
24 // set the parent - protected function as this has to be
25 // called only by add_child
26 void set_parent (DocumentTree *parent) {
27 this->parent = parent;
28 }
29
30 public:
31 // create new top level document tree
32 DocumentTree (std::string title, std::string stub = "") {
33 this->title = title;
34 // if stub is not empty set it
35 if (stub != "")
36 this->stub = stub;
37 // make the stub from the title
38 else
39 this->stub = convert_title (title);
40 this->parent = nullptr;
41 }
42
43 // create the document index for this tree
44 void create_tree_html (std::string destdir);
45
46 // set the title
47 void set_title (std::string title) {
48 this->title = title;
49 // if no stub is set
50 if (this->stub == "")
51 this->stub = convert_title (title);
52 }
53
54 void set_stub (std::string stub) {
55 this->stub = stub;
56 }
57
58 std::string get_title () {
59 return this->title;
60 }
61
62 std::string get_stub () {
63 return this->stub;
64 }
65
66 // get the child level of this tree
67 unsigned int get_level ();
68
69 // get the stub hierarchy
70 std::string stub_hierarchy ();
71
72 // add a child tree to this tree
73 void add_child (DocumentTree *child) {
74 child->set_parent (this);
75 this->children.push_back (*child);
76 }
77
78 // add a document to this tree
79 void add_document (Document *doc) {
80 this->docs.push_back (*doc);
81 }
82
83 // print a visual representation of this tree with levels
84 void visualize_tree ();
85
86 // get a pointer to the parent of this tree
87 DocumentTree *get_parent () {
88 return this->parent;
89 }
90 };
91
92 // get the tree level - 0 if top level
93 unsigned int DocumentTree::get_level () {
94 unsigned int lev = 0;
95 DocumentTree *par = this->get_parent ();
96 while (par != nullptr) {
97 lev ++;
98 par = par->get_parent ();
99 }
100 return lev;
101 }
102
103 // get the stub hierarchy for this tree
104 std::string DocumentTree::stub_hierarchy () {
105 std::list<std::string> levels;
106 DocumentTree *par = this->get_parent();
107 while (par!= nullptr) {
108 levels.push_front (par->get_stub());
109 par = par->get_parent ();
110 }
111 std::string stub_str;
112 for (std::string level : levels) {
113 // if stub is empty, don't append a /
114 if (level != "")
115 stub_str += level + "/";
116 }
117 return stub_str;
118 }
119
120 // print the representation of this tree
121 void DocumentTree::visualize_tree () {
122 // print the tree level
123 std::cout << std::setw(3) << std::left << this->get_level ();
124 // indent as per the level
125 for (unsigned int i = 0; i < this->get_level(); i ++)
126 std::cout << "+--";
127 // print the title of this tree
128 std::cout << this->title << std::endl;
129 // recurse through the child trees if any and so on
130 for (DocumentTree child : children)
131 child.visualize_tree ();
132 }
133
134 // create the tree - the index file for this tree and all the documents and
135 // the child trees recursively
136 void DocumentTree::create_tree_html (std::string destdir) {
137 std::unique_ptr<Document> index (new Document (this->title));
138 index.get()->set_index ();
139 // set the file name path
140 std::string filepath = destdir + "/" + this->stub_hierarchy () + this->stub;
141 // set the url path - this should not have destdir as it is not part
142 // of the site tree
143 std::string urlpath = this->stub_hierarchy () + this->stub;
144 // if urlpath is not empty then append a / to the end of the URL. This
145 // is so that the base URL is not absolute
146 if (urlpath != "")
147 urlpath += "/";
148
149 // create the sidebars
150 // First sidebar
151 // Create a link to the index page and
152 // If this tree has a parent, create a sidebar link to the level up
153 std::unique_ptr<SideBar> bar1 (new SideBar());
154 SideBarItem item0;
155 item0.set_sidebar_text (INDEX);
156 item0.set_sidebar_url (urlpath + "index.html");
157 bar1.get()->add_sidebar_item (item0);
158 if (this->get_parent() != nullptr) {
159 SideBarItem item1;
160 item1.set_sidebar_text (GO_UP);
161 item1.set_sidebar_url (this->stub_hierarchy() + "index.html");
162 bar1.get()->add_sidebar_item (item1);
163 }
164 index.get()->add_side_bar (*bar1.get());
165
166 // create a sidebar for the child levels if there are children
167 std::unique_ptr<SideBar> bar2 (new SideBar ());
168 bar2.get()->set_title (SUB_CAT + this->title);
169 for (DocumentTree tree : this->children) {
170 // we use site relative URLs that rely on the base href tag
171 // so for biaweb generated sites, the base href tag should be
172 // used in the main template
173 SideBarItem item (tree.get_title(), urlpath +
174 tree.stub + "/" + "index.html");
175 bar2.get()->add_sidebar_item (item);
176 }
177 index.get()->add_side_bar (*bar2.get());
178
179 // create the path and then the index file
180 std::filesystem::create_directories (filepath);
181
182 // Create the list of documents in this tree with links
183 // Reuse the sidebar class and sidebaritem class which is
184 // basically a list of links but instead of adding a sidebar
185 // add it to the content portion
186 SideBar article_list;
187 article_list.set_title (this->title + ": " + ART_LIST);
188 for (Document doc : this->docs) {
189 SideBarItem item;
190 item.set_sidebar_text (doc.get_title());
191 item.set_sidebar_url (urlpath + doc.get_filename() + ".html");
192 article_list.add_sidebar_item (item);
193 // output the document also, add the side bars
194 if (this->get_parent() != nullptr)
195 doc.add_side_bar (*bar1.get());
196 doc.add_side_bar (*bar2.get());
197 doc.output_to_html (filepath);
198 }
199 index.get()->set_content (article_list.to_html());
200
201 index.get()->output_to_html (filepath);
202
203 // create index for children
204 for (DocumentTree tree : this->children)
205 tree.create_tree_html (destdir);
206 }
207 }
208
209 #endif