First Commit
authorHarishankar <v.harishankar@gmail.com>
Fri, 15 May 2020 17:00:57 +0000 (22:30 +0530)
committerHarishankar <v.harishankar@gmail.com>
Fri, 15 May 2020 17:00:57 +0000 (22:30 +0530)
First commit of BiaWeb2 project

CMakeLists.txt [new file with mode: 0644]
biaweb [new file with mode: 0755]
biaweb.cpp [new file with mode: 0644]
biawebdocument.hpp [new file with mode: 0644]
biawebdocumenttree.hpp [new file with mode: 0644]
biawebsidebar.hpp [new file with mode: 0644]
biawebutil.hpp [new file with mode: 0644]
templates/main.tpl.html [new file with mode: 0644]
templates/sidebar.tpl.html [new file with mode: 0644]
templates/sidebaritem.tpl.html [new file with mode: 0644]
templates/sidebarlinkitem.tpl.html [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..cc14265
--- /dev/null
@@ -0,0 +1,6 @@
+cmake_minimum_required (VERSION 3.0)
+project (biaweb2 CXX)
+set (CMAKE_CXX_STANDARD 17)
+add_executable (biaweb biaweb.cpp)
+target_link_libraries (biaweb markdown)
+
diff --git a/biaweb b/biaweb
new file mode 100755 (executable)
index 0000000..3681f1b
Binary files /dev/null and b/biaweb differ
diff --git a/biaweb.cpp b/biaweb.cpp
new file mode 100644 (file)
index 0000000..a50bf01
--- /dev/null
@@ -0,0 +1,58 @@
+#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;
+}
diff --git a/biawebdocument.hpp b/biawebdocument.hpp
new file mode 100644 (file)
index 0000000..f001373
--- /dev/null
@@ -0,0 +1,202 @@
+#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
diff --git a/biawebdocumenttree.hpp b/biawebdocumenttree.hpp
new file mode 100644 (file)
index 0000000..9c91ed9
--- /dev/null
@@ -0,0 +1,160 @@
+#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
diff --git a/biawebsidebar.hpp b/biawebsidebar.hpp
new file mode 100644 (file)
index 0000000..5b73b75
--- /dev/null
@@ -0,0 +1,129 @@
+#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
diff --git a/biawebutil.hpp b/biawebutil.hpp
new file mode 100644 (file)
index 0000000..36aa0df
--- /dev/null
@@ -0,0 +1,42 @@
+#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 ("&amp;"); break;
+                case '<' : replace_buf.append ("&lt;"); break;
+                case '>' : replace_buf.append ("&gt;"); break;
+                case '\"': replace_buf.append ("&quot;"); break;
+                case '\'': replace_buf.append ("&apos;"); break;
+                default  : replace_buf.append (1, p); 
+            }
+        }
+        return replace_buf;
+    }
+}
+#endif
\ No newline at end of file
diff --git a/templates/main.tpl.html b/templates/main.tpl.html
new file mode 100644 (file)
index 0000000..197cb80
--- /dev/null
@@ -0,0 +1,75 @@
+<!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>
diff --git a/templates/sidebar.tpl.html b/templates/sidebar.tpl.html
new file mode 100644 (file)
index 0000000..a2e7c05
--- /dev/null
@@ -0,0 +1,4 @@
+<h2>%s</h2>
+<ul>
+%s
+</ul>
\ No newline at end of file
diff --git a/templates/sidebaritem.tpl.html b/templates/sidebaritem.tpl.html
new file mode 100644 (file)
index 0000000..c6209ee
--- /dev/null
@@ -0,0 +1 @@
+<li>%s</li>
\ No newline at end of file
diff --git a/templates/sidebarlinkitem.tpl.html b/templates/sidebarlinkitem.tpl.html
new file mode 100644 (file)
index 0000000..07afc4b
--- /dev/null
@@ -0,0 +1 @@
+<li><a href="%s">%s</a></li>
\ No newline at end of file