Added Navigation bit to the top of documents for the categories
[biaweb2.git] / biawebdocumenttree.hpp
1 #ifndef __BIAWEBDOCUMENTTREE__
2 #define __BIAWEBDOCUMENTTREE__
3 #include <memory>
4 #include <list>
5 #include <iostream>
6 #include <filesystem>
7 #include <sys/stat.h>
8 #include "biawebdocument.hpp"
9 #include "biawebstrings.hpp"
10 #include "biawebutil.hpp"
11 #include "biawebdoclist.hpp"
12
13 // class to implement a document tree - both with or without subtrees
14 namespace biaweb {
15 class DocumentTree {
16 protected:
17 // the pointer to the parent tree if there is one or nullptr
18 DocumentTree* parent;
19 // child trees
20 std::list<DocumentTree> children;
21 // title of this tree
22 std::string title;
23 // summary for this tree - this is displayed in the index.html file of
24 // this tree before the list of articles in the tree
25 std::string summary;
26 // file stub of this tree
27 std::string stub;
28 // list of documents in this tree
29 std::list<Document> docs;
30 // set the parent - protected function as this has to be
31 // called only by add_child
32 void set_parent (DocumentTree *parent) {
33 this->parent = parent;
34 }
35
36 public:
37 // method to build a document tree from a path
38 void document_tree_builder (std::string srcpath);
39
40 // create new top level document tree
41 DocumentTree (std::string title, std::string stub = "") {
42 this->title = escape_html (title);
43 // if stub is not empty set it
44 if (stub != "")
45 this->stub = stub;
46 // make the stub from the title
47 else
48 this->stub = convert_title (title);
49 this->parent = nullptr;
50 }
51
52 // set the summary for this tree
53 void set_summary (std::string summary) {
54 this->summary = summary;
55 }
56
57 // set the summary for this tree as markdown text
58 void set_markdown_summary (std::string summary) {
59 this->summary = convert_to_markdown (summary);
60 }
61
62 std::string get_summary () {
63 return this->summary;
64 }
65
66 // sort the documents as per creation time from latest to oldest
67 void sort_documents_creation_time () {
68 this->docs.sort ([] (Document &a, Document &b)
69 {return (a.get_creation_date() > b.get_creation_date()); });
70 }
71
72 // create the document index for this tree
73 void create_tree_html (std::string templatedir, std::string destdir);
74
75 // set the title
76 void set_title (std::string title) {
77 this->title = escape_html (title);
78 // if no stub is set
79 if (this->stub == "")
80 this->stub = convert_title (title);
81 }
82
83 void set_stub (std::string stub) {
84 this->stub = stub;
85 }
86
87 std::string get_title () {
88 return this->title;
89 }
90
91 std::string get_stub () {
92 return this->stub;
93 }
94
95 // get the child level of this tree
96 unsigned int get_level ();
97
98 // get the stub hierarchy
99 std::string stub_hierarchy ();
100
101 // add a child tree to this tree
102 void add_child (DocumentTree *child) {
103 child->set_parent (this);
104 this->children.push_back (*child);
105 }
106
107 // add a document to this tree
108 void add_document (Document *doc) {
109 this->docs.push_back (*doc);
110 }
111
112 // print a visual representation of this tree with levels
113 void visualize_tree ();
114
115 // get a pointer to the parent of this tree
116 DocumentTree *get_parent () {
117 return this->parent;
118 }
119 };
120
121 // get the tree level - 0 if top level
122 unsigned int DocumentTree::get_level () {
123 unsigned int lev = 0;
124 DocumentTree *par = this->get_parent ();
125 while (par != nullptr) {
126 lev ++;
127 par = par->get_parent ();
128 }
129 return lev;
130 }
131
132 // get the stub hierarchy for this tree
133 std::string DocumentTree::stub_hierarchy () {
134 std::list<std::string> levels;
135 DocumentTree *par = this->get_parent();
136 while (par!= nullptr) {
137 levels.push_front (par->get_stub());
138 par = par->get_parent ();
139 }
140 std::string stub_str;
141 for (std::string level : levels) {
142 // if stub is empty, don't append a /
143 if (level != "")
144 stub_str += level + "/";
145 }
146 return stub_str;
147 }
148
149 // print the representation of this tree
150 void DocumentTree::visualize_tree () {
151 // print the tree level
152 std::cout << std::setw(3) << std::left << this->get_level ();
153 // indent as per the level
154 for (unsigned int i = 0; i < this->get_level(); i ++)
155 std::cout << "+--";
156 // print the title of this tree
157 std::cout << this->title << std::endl;
158 // recurse through the child trees if any and so on
159 for (DocumentTree child : children)
160 child.visualize_tree ();
161 }
162
163 // create the tree - the index file for this tree and all the documents and
164 // the child trees recursively - using the template specified
165 void DocumentTree::create_tree_html (std::string templatedir, std::string destdir) {
166 // create a document to represent the index of the tree.
167 std::unique_ptr<Document> index (new Document (this->title));
168 index.get()->set_index ();
169 // set the file name path
170 std::string filepath = destdir + "/" + this->stub_hierarchy () + this->stub;
171 // set the url path - this should not have destdir as it is not part
172 // of the site tree
173 std::string urlpath = this->stub_hierarchy () + this->stub;
174 // if urlpath is not empty then append a / to the end of the URL. This
175 // is so that the base URL is not absolute
176 if (urlpath != "")
177 urlpath += "/";
178
179 // create the sidebars
180 // First sidebar
181 // Create a link to the index page and
182 // If this tree has a parent, create a sidebar link to the level up
183 std::unique_ptr<SideBar> bar1 (new SideBar());
184 GenericLinkItem item0;
185 bar1.get()->set_title (NAVIGATE);
186 item0.set_item_text (INDEX);
187 item0.set_item_url (urlpath + "index.html");
188 bar1.get()->add_sidebar_item (item0);
189 if (this->get_parent() != nullptr) {
190 GenericLinkItem item1;
191 item1.set_item_text (GO_UP);
192 item1.set_item_url (this->stub_hierarchy() + "index.html");
193 bar1.get()->add_sidebar_item (item1);
194 }
195 index.get()->add_side_bar (*bar1.get());
196
197 // create a sidebar for the child levels if there are children
198 std::unique_ptr<SideBar> bar2 (new SideBar ());
199 bar2.get()->set_title (SUB_CAT + this->title);
200 for (DocumentTree tree : this->children) {
201 // we use site relative URLs that rely on the base href tag
202 // so for biaweb generated sites, the base href tag should be
203 // used in the main template
204 GenericLinkItem item (tree.get_title(), urlpath +
205 tree.stub + "/" + "index.html");
206 bar2.get()->add_sidebar_item (item);
207 }
208 index.get()->add_side_bar (*bar2.get());
209
210 // create the path and then the index file
211 std::filesystem::create_directories (filepath);
212
213 // Create the list of documents in this tree with links
214 std::unique_ptr<DocList> article_list (new DocList ());
215 article_list.get()->set_title (this->title + ": " + ART_LIST);
216 // sort the documents as per creation time and then add the document
217 // links - newest documents should appear above older ones.
218 sort_documents_creation_time ();
219
220 // create the navigation bit
221 std::shared_ptr<NavigationBit> navbit (new NavigationBit ());
222 auto par1 = this;
223 // get the link to each level in the hierarchy and add it as
224 // an inline list
225 while (par1 != nullptr) {
226 if (par1->parent != nullptr)
227 navbit.get()->add_link_item (GenericLinkItem(par1->stub,
228 par1->stub_hierarchy() + par1->stub + "/index.html"));
229 else
230 navbit.get()->add_link_item (GenericLinkItem(HOME, "index.html"));
231 par1 = par1->parent;
232 }
233
234 for (Document doc : this->docs) {
235 DocListItem item (doc.get_title(),
236 urlpath + doc.get_filename() + ".html",
237 doc.get_creation_date(), doc.get_modified_date ());
238 article_list.get()->add_document_item (item);
239 // output the document also, add the navigation bit and side bars
240
241 doc.set_navigation_bit (*navbit.get());
242 doc.add_side_bar (*bar1.get());
243 doc.add_side_bar (*bar2.get());
244 doc.output_to_html (templatedir, filepath);
245 }
246
247 // add the navigation bit
248 index.get()->set_navigation_bit (*navbit.get());
249 // index should contain the summary followed by the article list
250 index.get()->set_content (this->summary + article_list.get()->to_html(templatedir));
251
252 // output the index file
253 index.get()->output_to_html (templatedir, filepath);
254
255 // recursively create index for children
256 for (DocumentTree tree : this->children)
257 tree.create_tree_html (templatedir, destdir);
258 }
259
260 // build a document tree from a filesystem path recursively
261 void DocumentTree::document_tree_builder (std::string srcpath_str) {
262 std::filesystem::path srcpath (srcpath_str);
263 this->title = srcpath.stem().string ();
264
265 // Get the directories to this child and add them as sub document
266 // trees
267 try {
268 for (auto fsitem : std::filesystem::directory_iterator (srcpath) )
269 {
270 // if it is a directory then build the tree for that directory
271 if (fsitem.is_directory ()) {
272 std::shared_ptr <DocumentTree> doctree
273 (new DocumentTree (fsitem.path().filename().string()));
274
275 this->add_child (doctree.get());
276 }
277 // add the regular files as documents in the tree
278 else if (fsitem.is_regular_file ()) {
279 // if it is an index file (specially named as index
280 // or index.md or whatever) directly add
281 // the contents to the summary of the Doctree
282 if (fsitem.path().stem().string() == "index")
283 {
284 std::ifstream infile (fsitem.path());
285 std::string infilestr ( (std::istreambuf_iterator<char> (infile)),
286 (std::istreambuf_iterator<char> ()) );
287 this->set_markdown_summary (infilestr);
288 }
289 // else it is a non-index file-
290 // create a Document and add it to the tree
291 else {
292 std::ifstream infile (fsitem.path ());
293 std::shared_ptr<Document> doc
294 (new Document (infile));
295 infile.close ();
296
297 // file modified date from system
298 struct stat buf;
299 if (stat (fsitem.path().string().c_str(), &buf) == 0)
300 doc.get()->set_modified_date (buf.st_mtim.tv_sec);
301
302 this->add_document (doc.get());
303 }
304 }
305 }
306 }
307 catch (std::filesystem::filesystem_error) {
308 std::cout << "No such path! Specify an existing file path" << std::endl;
309 }
310
311 // add the trees for the children recursively
312 for (DocumentTree &child : this->children)
313 child.document_tree_builder (srcpath_str + "/" + child.title);
314 }
315 }
316
317 #endif