From: Harishankar Date: Fri, 15 May 2020 17:00:57 +0000 (+0530) Subject: First Commit X-Git-Url: https://harishankar.org/repos/?p=biaweb2.git;a=commitdiff_plain;h=9dd960dd37c92af1ca6531c72b82849ea59a354c First Commit First commit of BiaWeb2 project --- 9dd960dd37c92af1ca6531c72b82849ea59a354c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..cc14265 --- /dev/null +++ b/CMakeLists.txt @@ -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 index 0000000..3681f1b Binary files /dev/null and b/biaweb differ diff --git a/biaweb.cpp b/biaweb.cpp new file mode 100644 index 0000000..a50bf01 --- /dev/null +++ b/biaweb.cpp @@ -0,0 +1,58 @@ +#include +#include +#include "biawebdocumenttree.hpp" +using namespace biaweb; + +int main (int argc, char *argv[]) { + + if (argc == 2) + { + std::shared_ptr tree (new DocumentTree (argv[1])); + + std::shared_ptr a1 (new DocumentTree("Child a1")); + std::shared_ptr a2 (new DocumentTree("Child a2")); + std::shared_ptr a3 (new DocumentTree("Child a3")); + std::shared_ptr 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] << "
" << 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 (f)), + // (std::istreambuf_iterator ()) ); + + // 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 index 0000000..f001373 --- /dev/null +++ b/biawebdocument.hpp @@ -0,0 +1,202 @@ +#ifndef __BIAWEB__ +#define __BIAWEB__ +#include +#include +#include +#include +#include +#include +#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 +} +// 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 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 (ftmp)), + (std::istreambuf_iterator ()) + ); + + 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 (tpl)), + (std::istreambuf_iterator ()) ); + 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 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 index 0000000..9c91ed9 --- /dev/null +++ b/biawebdocumenttree.hpp @@ -0,0 +1,160 @@ +#ifndef __BIAWEBDOCUMENTTREE__ +#define __BIAWEBDOCUMENTTREE__ +#include +#include +#include +#include +#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 children; + // title of this tree + std::string title; + // file stub of this tree + std::string stub; + // list of documents in this tree + std::list 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 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 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 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 index 0000000..5b73b75 --- /dev/null +++ b/biawebsidebar.hpp @@ -0,0 +1,129 @@ +#ifndef __BIAWEBSIDEBAR__ +#define __BIAWEBSIDEBAR__ +#include +#include +#include +#include +#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 (tpl_linkitem)), + (std::istreambuf_iterator ())); + tpl_linkitem.close (); + std::unique_ptr 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 (tpl_item)), + (std::istreambuf_iterator ())); + tpl_item.close (); + std::unique_ptr 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 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 (sidetpl)) , + (std::istreambuf_iterator ())); + 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 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 index 0000000..36aa0df --- /dev/null +++ b/biawebutil.hpp @@ -0,0 +1,42 @@ +#ifndef __BIAWEBUTIL__ +#define __BIAWEBUTIL__ +#include + +// 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 diff --git a/templates/main.tpl.html b/templates/main.tpl.html new file mode 100644 index 0000000..197cb80 --- /dev/null +++ b/templates/main.tpl.html @@ -0,0 +1,75 @@ + + + +%s + + + + + +
+ +
+
Created on: %s, last modified: %s
+ %s +
+ + +
+ + + diff --git a/templates/sidebar.tpl.html b/templates/sidebar.tpl.html new file mode 100644 index 0000000..a2e7c05 --- /dev/null +++ b/templates/sidebar.tpl.html @@ -0,0 +1,4 @@ +

%s

+
    +%s +
\ No newline at end of file diff --git a/templates/sidebaritem.tpl.html b/templates/sidebaritem.tpl.html new file mode 100644 index 0000000..c6209ee --- /dev/null +++ b/templates/sidebaritem.tpl.html @@ -0,0 +1 @@ +
  • %s
  • \ No newline at end of file diff --git a/templates/sidebarlinkitem.tpl.html b/templates/sidebarlinkitem.tpl.html new file mode 100644 index 0000000..07afc4b --- /dev/null +++ b/templates/sidebarlinkitem.tpl.html @@ -0,0 +1 @@ +
  • %s
  • \ No newline at end of file