--- /dev/null
+cmake_minimum_required (VERSION 3.0)
+project (biaweb2 CXX)
+set (CMAKE_CXX_STANDARD 17)
+add_executable (biaweb biaweb.cpp)
+target_link_libraries (biaweb markdown)
+
--- /dev/null
+#include <iostream>
+#include <fstream>
+#include "biawebdocumenttree.hpp"
+using namespace biaweb;
+
+int main (int argc, char *argv[]) {
+
+ if (argc == 2)
+ {
+ std::shared_ptr<DocumentTree> tree (new DocumentTree (argv[1]));
+
+ std::shared_ptr<DocumentTree> a1 (new DocumentTree("Child a1"));
+ std::shared_ptr<DocumentTree> a2 (new DocumentTree("Child a2"));
+ std::shared_ptr<DocumentTree> a3 (new DocumentTree("Child a3"));
+ std::shared_ptr<DocumentTree> a4 (new DocumentTree("Child a4"));
+ a3.get()->add_child (a4.get());
+ a1.get()->add_child (a2.get());
+ a1.get()->add_child (a3.get());
+ tree.get()->add_child (a1.get());
+ std::cout << a3.get()->stub_hierarchy () << a3.get()->get_stub () << std::endl;
+ tree.get()->visualize_tree ();
+ tree.get()->create_index ();
+ }
+ else
+ std::cout << "Usage: " << argv[0] << " <main tree>" << std::endl;
+
+ // Document doc;
+ // SideBar items;
+ // std::string title, contents, sidetitle;
+ // std::cout << "Enter document title: ";
+ // std::getline (std::cin, title);
+ // std::cout << "Enter markdown file of document: ";
+ // std::getline (std::cin, contents);
+ // while (1) {
+ // std::string name, url;
+ // std::cout << "Enter a sidebar item text (empty to end): ";
+ // std::getline (std::cin, name);
+ // if (name.empty()) break;
+ // std::cout << "Enter a sidebar item URL: ";
+ // std::getline (std::cin, url);
+ // SideBarItem item (name, url);
+ // items.add_sidebar_item (item);
+ // }
+ // std::cout << "Enter heading for sidebar: ";
+ // std::getline (std::cin, sidetitle);
+ // items.set_title (sidetitle);
+ // doc.set_title (title);
+ // std::ifstream f (contents);
+ // std::string markdown_contents ( (std::istreambuf_iterator<char> (f)),
+ // (std::istreambuf_iterator<char> ()) );
+
+ // doc.set_markdown_content (markdown_contents);
+ // doc.add_side_bar (items);
+
+ // doc.output_to_html ();
+
+ return 0;
+}
--- /dev/null
+#ifndef __BIAWEB__
+#define __BIAWEB__
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <list>
+#include <memory>
+#include <ctime>
+#include "biawebutil.hpp"
+#include "biawebsidebar.hpp"
+
+// "discount" markdown library is a C library and hence requires to be wrapped in
+// extern "C"
+extern "C" {
+ #include <mkdio.h>
+}
+// class to represent a biaweb document which can have a file name, title, description,
+// keywords, content and sidebar items
+namespace biaweb {
+ class Document
+ {
+ protected:
+ std::string filename;
+ std::string title;
+ std::string meta_desc;
+ std::string meta_keywords;
+ std::string content;
+ std::list<SideBar> sidebars;
+ std::time_t cdate;
+ std::time_t mdate;
+ bool is_index;
+
+ public:
+ Document (std::string title = "", std::string meta_desc = "",
+ std::string meta_keywords = "", std::string content = "",
+ bool is_index = false, std::time_t cdate= std::time(nullptr),
+ std::time_t mdate = std::time(nullptr))
+ {
+ this->title = title;
+ this->meta_desc = meta_desc;
+ this->meta_keywords = meta_keywords;
+ this->content = content;
+ this->is_index = is_index;
+ if (is_index)
+ this->filename = convert_title (title);
+ else
+ this->filename = "index";
+ this->cdate = cdate;
+ this->mdate = mdate;
+ }
+
+ // set whether this is the index document
+ void set_index (bool index = true) {
+ this->is_index = index;
+ }
+
+ // get whether this is the index document
+ bool get_index () {
+ return this->is_index;
+ }
+
+ // set the document modification date
+ void set_modified_date (std::time_t modif) {
+ this->mdate = modif;
+ }
+
+ // get the document modification date
+ std::time_t get_modified_date () {
+ return this->mdate;
+ }
+
+ // set the document creation date
+ void set_creation_date (std::time_t creat) {
+ this->cdate = creat;
+ }
+
+ // get the document creation date
+ std::time_t get_creation_date () {
+ return this->cdate;
+ }
+
+ // output the document to HTML using the template
+ void output_to_html (std::string path);
+
+ // set the content portion of document as raw HTML content
+ void set_content (std::string content) {
+ this->content = content;
+ }
+
+ // read the contents of marked marked-up content string "str" into the
+ // contents after converting to HTML.
+ void set_markdown_content (std::string str);
+
+ void set_meta_keywords(std::string meta_keywords) {
+ this->meta_keywords = meta_keywords;
+ }
+
+ void set_meta_desc(std::string meta_desc) {
+ this->meta_desc = meta_desc;
+ }
+
+ void set_title(std::string title) {
+ this->title = title;
+ if (this->is_index)
+ this->filename = "index";
+ else
+ this->filename = convert_title (title);
+ }
+
+ std::string get_content () {
+ return this->content;
+ }
+
+ std::string get_meta_keywords() {
+ return this->meta_keywords;
+ }
+
+ std::string get_meta_desc() {
+ return this->meta_desc;
+ }
+
+ std::string get_title() {
+ return this->title;
+ }
+
+ void add_side_bar (SideBar bar) {
+ sidebars.insert (sidebars.cend(), bar);
+ }
+ };
+
+ void Document::set_markdown_content (std::string str) {
+ // discount is a C library and it doesn't work well with C++ streams
+ // and there seems no way to get the output of any of these functions
+ // into an std::string.
+ // the only option seems to be to write the output of the markdown()
+ // function to a temporary working file and then read it back into C++
+ // with the normal std::ifstream and feed it into the std::string
+ // till a cleaner solution can be found.
+ MMIOT *doc;
+ doc = mkd_string (str.c_str(), str.size(), 0);
+ FILE *f = fopen (".biaweb.tmp", "w");
+ markdown (doc, f, 0);
+ fclose (f);
+ std::ifstream ftmp (".biaweb.tmp");
+ std::string tmpl ( (std::istreambuf_iterator<char> (ftmp)),
+ (std::istreambuf_iterator<char> ())
+ );
+
+ while (! ftmp.eof ())
+ {
+ std::string line;
+ ftmp >> line;
+ tmpl.append (line);
+ tmpl.append (" ");
+ }
+ ftmp.close ();
+ remove (".biaweb.tmp");
+ this->content.append (tmpl);
+ mkd_cleanup (doc);
+ }
+
+ void Document::output_to_html (std::string path)
+ {
+ std::ifstream tpl;
+ tpl.open ("templates/main.tpl.html", std::ios_base::openmode::_S_in);
+ std::string main_tpl ( (std::istreambuf_iterator<char> (tpl)),
+ (std::istreambuf_iterator<char> ()) );
+ tpl.close ();
+ // first render the sidebars
+ std::string sidebartext;
+ for (SideBar bar : sidebars) {
+ sidebartext += bar.to_html ();
+ }
+
+ char ctm_str[100], mtm_str[100];
+ std::time_t creat = this->cdate;
+ std::time_t modif = this->cdate;
+ std::strftime (ctm_str, sizeof (ctm_str),
+ "%d %b %Y, %H:%M", std::localtime (&creat));
+ std::strftime (mtm_str, sizeof (mtm_str),
+ "%d %b %Y, %H:%M", std::localtime (&modif));
+
+ // Allocate enough space for the output buffer
+ std::unique_ptr<char[]> final_templ(
+ new char[main_tpl.size()+
+ this->title.size()+
+ this->content.size() +
+ this->meta_desc.size() +
+ this->meta_keywords.size () +
+ 200 +
+ sidebartext.size()]);
+ std::sprintf (final_templ.get (), main_tpl.c_str(), this->title.c_str(),
+ ctm_str, mtm_str,
+ this->content.c_str(), sidebartext.c_str());
+
+ std::ofstream f (path + "/" + this->filename + ".html");
+ f << final_templ.get ();
+ f.close ();
+ }
+}
+
+#endif
--- /dev/null
+#ifndef __BIAWEBDOCUMENTTREE__
+#define __BIAWEBDOCUMENTTREE__
+#include <memory>
+#include <list>
+#include <iostream>
+#include <filesystem>
+#include "biawebdocument.hpp"
+
+// to implement a document tree - both with or without subtrees
+namespace biaweb {
+ class DocumentTree {
+ protected:
+ // the pointer to the parent tree if there is one or nullptr
+ DocumentTree* parent;
+ // child trees
+ std::list<DocumentTree> children;
+ // title of this tree
+ std::string title;
+ // file stub of this tree
+ std::string stub;
+ // list of documents in this tree
+ std::list<Document> docs;
+ // set the parent - protected function as this has to be
+ // called only by add_child
+ void set_parent (DocumentTree *parent) {
+ this->parent = parent;
+ }
+
+ public:
+ // create new top level document tree
+ DocumentTree (std::string title, std::string stub = "") {
+ this->title = title;
+ // if stub is not empty set it
+ if (stub != "")
+ this->stub = stub;
+ // make the stub from the title
+ else
+ this->stub = convert_title (title);
+ this->parent = nullptr;
+ }
+
+ // create the document index for this tree
+ void create_index ();
+
+ // set the title
+ void set_title (std::string title) {
+ this->title = title;
+ // if no stub is set
+ if (this->stub == "")
+ this->stub = convert_title (title);
+ }
+
+ // set the stub either from a text or convert the title to stub
+ // if no stub is set explicitly
+ void set_stub (std::string stub) {
+ if (stub != "")
+ this->stub = stub;
+ else
+ this->stub = convert_title (this->title);
+ }
+
+ std::string get_title () {
+ return this->title;
+ }
+
+ std::string get_stub () {
+ return this->stub;
+ }
+
+ // get the child level of this tree
+ unsigned int get_level ();
+
+ // get the stub hierarchy
+ std::string stub_hierarchy () {
+ std::list<std::string> levels;
+ DocumentTree *par = this->get_parent();
+ while (par!= nullptr) {
+ levels.push_front (par->get_stub());
+ par = par->get_parent ();
+ }
+ std::string stub_str;
+ for (std::string level : levels) {
+ stub_str += level + "/";
+ }
+ return stub_str;
+ }
+
+ // add a child tree to this tree
+ void add_child (DocumentTree *child) {
+ child->set_parent (this);
+ this->children.push_back (*child);
+ }
+
+ // add a document to this tree
+ void add_document (Document *doc) {
+ this->docs.push_back (*doc);
+ }
+
+ // print a visual representation of this tree with levels
+ void visualize_tree ();
+
+ // get a pointer to the parent of this tree
+ DocumentTree *get_parent () {
+ return this->parent;
+ }
+ };
+
+ // get the tree level - 0 if top level
+ unsigned int DocumentTree::get_level () {
+ unsigned int lev = 0;
+ DocumentTree *par = this->get_parent ();
+ while (par != nullptr) {
+ lev ++;
+ par = par->get_parent ();
+ }
+ return lev;
+ }
+
+ // print the representation of this tree
+ void DocumentTree::visualize_tree () {
+ // print the tree level
+ std::cout << std::setw(3) << std::left << this->get_level ();
+ // indent as per the level
+ for (unsigned int i = 0; i < this->get_level(); i ++)
+ std::cout << "+--";
+ // print the title of this tree
+ 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::unique_ptr<Document> index (new Document (this->title));
+ index.get()->set_index ();
+ // set the file name
+ std::string filepath = this->stub_hierarchy () +
+ this->stub;
+ // create the sidebar
+ std::unique_ptr<SideBar> bar (new SideBar ());
+ bar.get()->set_title (this->title);
+ for (DocumentTree tree : this->children) {
+ SideBarItem item (tree.get_title(), filepath + "/" +
+ tree.stub + "/" + "index.html");
+ bar.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
+ for (DocumentTree tree : this->children)
+ tree.create_index ();
+ }
+}
+
+#endif
--- /dev/null
+#ifndef __BIAWEBSIDEBAR__
+#define __BIAWEBSIDEBAR__
+#include <string>
+#include <list>
+#include <iostream>
+#include <fstream>
+#include "biawebutil.hpp"
+
+// classes to describe the sidebar and sidebar item containers which form part of
+// main document
+namespace biaweb {
+ // class to represent a sidebar item which can contain a text and link or only
+ // text
+ class SideBarItem {
+ protected:
+ // sidebar text and url
+ std::string sidebar_text;
+ std::string sidebar_url;
+ public:
+ std::string get_sidebar_text () {
+ return this->sidebar_text;
+ }
+ void set_sidebar_text (std::string text) {
+ this->sidebar_text = text;
+ }
+ std::string get_sidebar_url () {
+ return this->sidebar_url;
+ }
+ void set_sidebar_url (std::string url) {
+ this->sidebar_url = url;
+ }
+ std::string to_html ();
+
+ SideBarItem (std::string text = "", std::string url = "") {
+ this->sidebar_text = text;
+ this->sidebar_url = url;
+ }
+ };
+
+ std::string SideBarItem::to_html () {
+ std::string html;
+ // if url is not empty it is a link item so load the sidebar link template
+ if (! this->sidebar_url.empty ()) {
+ if (!this->sidebar_text.empty ())
+ {
+ std::ifstream tpl_linkitem ("templates/sidebarlinkitem.tpl.html");
+ std::string tpl_linkitem_str ( (std::istreambuf_iterator<char> (tpl_linkitem)),
+ (std::istreambuf_iterator<char> ()));
+ tpl_linkitem.close ();
+ std::unique_ptr<char[]> linktxt (new char[tpl_linkitem_str.size()
+ + this->sidebar_text.size ()
+ + this->sidebar_url.size ()] );
+ std::sprintf (linktxt.get(), tpl_linkitem_str.c_str (),
+ this->sidebar_url.c_str (),
+ this->sidebar_text.c_str ());
+ html.append (linktxt.get ());
+ }
+ // no text or url - item is empty - so it should be blank
+ else
+ html = "";
+ }
+ // Non link item. Load the normal sidebar item template.
+ else
+ {
+ std::ifstream tpl_item ("templates/sidebaritem.tpl.html");
+ std::string tpl_item_str ( (std::istreambuf_iterator<char> (tpl_item)),
+ (std::istreambuf_iterator<char> ()));
+ tpl_item.close ();
+ std::unique_ptr<char[]> txt (new char [tpl_item_str.size () +
+ this->sidebar_text.size ()]);
+ std::sprintf (txt.get (), tpl_item_str.c_str(), this->sidebar_text.c_str());
+ html.append (txt.get ());
+ }
+ return html;
+ }
+
+ // Class to represent a sidebar, which contains one heading and a list of items
+ // either links or non-link items.
+ class SideBar {
+ protected:
+ std::string sidebar_title;
+ std::list <SideBarItem> items;
+
+ public:
+ void set_title (std::string title) {
+ this->sidebar_title = title;
+ }
+
+ void add_sidebar_item (SideBarItem item) {
+ items.insert (items.cend(), item);
+ }
+
+ // render the sidebar
+ std::string to_html () ;
+ };
+
+ // render the sidebar to HTML representation from the template.
+ std::string SideBar::to_html () {
+ std::ifstream sidetpl ("templates/sidebar.tpl.html");
+ std::string sidetpl_str ( ( std::istreambuf_iterator<char> (sidetpl)) ,
+ (std::istreambuf_iterator<char> ()));
+ sidetpl.close ();
+ std::string listitem;
+ // first get the sidebar items and render them to HTML
+ for (SideBarItem item : items) {
+ listitem += item.to_html ();
+ }
+
+ std::unique_ptr<char[]> tpl_final (new char[sidetpl_str.size() +
+ this->sidebar_title.size () +
+ listitem.size () ]);
+
+ std::string html;
+ // if there are items, sidebar should be rendered
+ if (items.size () > 0)
+ {
+ std::sprintf (tpl_final.get (), sidetpl_str.c_str(),
+ this->sidebar_title.c_str(), listitem.c_str()) ;
+ html.append ( tpl_final.get ());
+ }
+ // no items in the sidebar, render as empty string (even if it has a heading)
+ // since heading becomes meaningless without items
+ else
+ html = "";
+ return html;
+ }
+}
+
+#endif
--- /dev/null
+#ifndef __BIAWEBUTIL__
+#define __BIAWEBUTIL__
+#include <string>
+
+// utility functions for Biaweb that don't fit into any class and can be used by
+// any class
+namespace biaweb {
+ // convert a document title to a file title - strip out the non-alpha
+ // chars and spaces
+ std::string convert_title (std::string title)
+ {
+ std::string output;
+ for (char c : title) {
+ if (isalnum (c))
+ output.append (1, c);
+ else if (isspace (c))
+ output.append (1, '_');
+ }
+ return output;
+ }
+
+ // escape HTML special characters
+ std::string escape_html (std::string source)
+ {
+ std::string replace_buf;
+ replace_buf.reserve (source.size());
+ for (char p : source)
+ {
+ switch (p)
+ {
+ case '&' : replace_buf.append ("&"); break;
+ case '<' : replace_buf.append ("<"); break;
+ case '>' : replace_buf.append (">"); break;
+ case '\"': replace_buf.append ("""); break;
+ case '\'': replace_buf.append ("'"); break;
+ default : replace_buf.append (1, p);
+ }
+ }
+ return replace_buf;
+ }
+}
+#endif
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<title>%s</title>
+<!-- change base href to your site URL -->
+<base href="http://my.site/">
+<style>
+ body {
+ background-color: whitesmoke;
+ font-family:Arial, Helvetica, sans-serif;
+ font-size: 0.9em;
+ margin: 0;
+ padding: 0;
+ }
+ h1, h2, h3, h4, h5, h6 {
+ font-family: Georgia, 'Times New Roman', Times, serif;
+ }
+ div#header {
+ width: 100%;
+ color: white;
+ background-color:#a1a0c0;
+ height: 50px;
+ }
+ div#modification {
+ width: 100%;
+ color: darkslateblue;
+ font-size: 0.9em;
+ }
+ div#footer {
+ width: 100%;
+ text-align: center;
+ background-color: #a1a0c0;
+ color: white;
+ float:inline-end;
+ }
+ div#sidebar {
+ float :inline-end;
+ background-color: darkslateblue;
+ color: white;
+ width: 32%;
+ padding: 1%;
+ }
+ div#sidebar a, a:visited {
+ color: white;
+ }
+ div#main {
+ width: 100%;
+ float: none;
+ color: black;
+ }
+ div#content {
+ width: 63%;
+ margin-right:1%;
+ margin-left: 2%;
+ float:inline-start;
+ }
+</style>
+</head>
+<body>
+<div id="main">
+ <div id="header"></div>
+ <div id="content">
+ <div id="modification">Created on: %s, last modified: %s</div>
+ %s
+ </div>
+ <div id="sidebar">
+ %s
+ </div>
+ <div id ="footer">
+ My copyright
+ </div>
+</div>
+
+</body>
+</html>
--- /dev/null
+<h2>%s</h2>
+<ul>
+%s
+</ul>
\ No newline at end of file
--- /dev/null
+<li>%s</li>
\ No newline at end of file
--- /dev/null
+<li><a href="%s">%s</a></li>
\ No newline at end of file