aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-02-28 16:49:17 +0200
committerKaren Arutyunov <karen@codesynthesis.com>2016-03-02 17:34:10 +0300
commit4fec7e73201ed50e4a4157cb1ea1f1c63566dd89 (patch)
tree93ca4fc57d2ba8bf077ac021cf96fa6335c8bfcc
parentcfdbf0f9c52288efaa57eba3a9c913790f034cf2 (diff)
Menu customization
-rw-r--r--brep/buildfile1
-rw-r--r--brep/mod-package-details.cxx5
-rw-r--r--brep/mod-package-search.cxx5
-rw-r--r--brep/mod-package-version-details.cxx5
-rw-r--r--brep/mod-repository-details.cxx5
-rw-r--r--brep/options-types10
-rw-r--r--brep/options.cli31
-rw-r--r--brep/page52
-rw-r--r--brep/page.cxx41
-rw-r--r--brep/types-parsers16
-rw-r--r--brep/types-parsers.cxx51
-rw-r--r--etc/brep-module.conf14
-rw-r--r--web/xhtml-fragment48
-rw-r--r--web/xhtml-fragment.cxx115
14 files changed, 357 insertions, 42 deletions
diff --git a/brep/buildfile b/brep/buildfile
index b77952c..360f82d 100644
--- a/brep/buildfile
+++ b/brep/buildfile
@@ -58,6 +58,7 @@ src = \
../web/{hxx cxx}{ mime-url-encoding } \
../web/{hxx }{ module } \
../web/{hxx }{ xhtml } \
+ ../web/{hxx cxx}{ xhtml-fragment } \
../web/apache/{hxx }{ log } \
../web/apache/{hxx ixx cxx}{ request } \
../web/apache/{hxx txx cxx}{ service } \
diff --git a/brep/mod-package-details.cxx b/brep/mod-package-details.cxx
index 98b2135..fa07dd1 100644
--- a/brep/mod-package-details.cxx
+++ b/brep/mod-package-details.cxx
@@ -12,6 +12,7 @@
#include <web/xhtml>
#include <web/module>
+#include <web/xhtml-fragment>
#include <web/mime-url-encoding>
#include <brep/page>
@@ -65,6 +66,8 @@ handle (request& rq, response& rs)
//
static const size_t res_page (options_->search_results ());
static const dir_path& root (options_->root ());
+ static const fragment& logo (options_->logo ());
+ static const vector<page_menu>& menu (options_->menu ());
const string& name (*rq.path ().rbegin ());
const string ename (mime_url_encode (name));
@@ -127,7 +130,7 @@ handle (request& rq, response& rs)
<< SCRIPT << " " << ~SCRIPT
<< ~HEAD
<< BODY
- << DIV_HEADER (root)
+ << DIV_HEADER (root, logo, menu)
<< DIV(ID="content");
if (full)
diff --git a/brep/mod-package-search.cxx b/brep/mod-package-search.cxx
index 5725da0..7505e02 100644
--- a/brep/mod-package-search.cxx
+++ b/brep/mod-package-search.cxx
@@ -13,6 +13,7 @@
#include <web/xhtml>
#include <web/module>
+#include <web/xhtml-fragment>
#include <web/mime-url-encoding>
#include <brep/version>
@@ -78,6 +79,8 @@ handle (request& rq, response& rs)
//
static const size_t res_page (options_->search_results ());
static const dir_path& root (options_->root ());
+ static const fragment& logo (options_->logo ());
+ static const vector<page_menu>& menu (options_->menu ());
params::package_search params;
@@ -121,7 +124,7 @@ handle (request& rq, response& rs)
<< SCRIPT << " " << ~SCRIPT
<< ~HEAD
<< BODY
- << DIV_HEADER (root)
+ << DIV_HEADER (root, logo, menu)
<< DIV(ID="content");
session sn;
diff --git a/brep/mod-package-version-details.cxx b/brep/mod-package-version-details.cxx
index 5090a19..c9055a5 100644
--- a/brep/mod-package-version-details.cxx
+++ b/brep/mod-package-version-details.cxx
@@ -12,6 +12,7 @@
#include <web/xhtml>
#include <web/module>
+#include <web/xhtml-fragment>
#include <web/mime-url-encoding>
#include <brep/page>
@@ -51,6 +52,8 @@ handle (request& rq, response& rs)
// server process.
//
static const dir_path& root (options_->root ());
+ static const fragment& logo (options_->logo ());
+ static const vector<page_menu>& menu (options_->menu ());
auto i (rq.path ().rbegin ());
version ver;
@@ -104,7 +107,7 @@ handle (request& rq, response& rs)
<< CSS_LINKS (path ("package-version-details.css"), root)
<< ~HEAD
<< BODY
- << DIV_HEADER (root)
+ << DIV_HEADER (root, logo, menu)
<< DIV(ID="content");
if (full)
diff --git a/brep/mod-repository-details.cxx b/brep/mod-repository-details.cxx
index 742bee9..0a88a16 100644
--- a/brep/mod-repository-details.cxx
+++ b/brep/mod-repository-details.cxx
@@ -18,6 +18,7 @@
#include <web/xhtml>
#include <web/module>
+#include <web/xhtml-fragment>
#include <web/mime-url-encoding>
#include <brep/page>
@@ -57,6 +58,8 @@ handle (request& rq, response& rs)
// server process.
//
static const dir_path& root (options_->root ());
+ static const fragment& logo (options_->logo ());
+ static const vector<page_menu>& menu (options_->menu ());
// Make sure no parameters passed.
//
@@ -79,7 +82,7 @@ handle (request& rq, response& rs)
<< CSS_LINKS (path ("repository-details.css"), root)
<< ~HEAD
<< BODY
- << DIV_HEADER (root)
+ << DIV_HEADER (root, logo, menu)
<< DIV(ID="content");
transaction t (db_->begin ());
diff --git a/brep/options-types b/brep/options-types
index 767423c..922637b 100644
--- a/brep/options-types
+++ b/brep/options-types
@@ -17,6 +17,16 @@ namespace brep
full,
brief
};
+
+ struct page_menu
+ {
+ string label;
+ string link;
+
+ page_menu () = default;
+ page_menu (string b, string l): label (move (b)), link (move (l)) {}
+ };
+
}
#endif // BREP_OPTIONS_TYPES
diff --git a/brep/options.cli b/brep/options.cli
index 2da6718..986432a 100644
--- a/brep/options.cli
+++ b/brep/options.cli
@@ -2,6 +2,8 @@
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
+include <web/xhtml-fragment>;
+
include <brep/types>;
include <brep/options-types>;
@@ -70,6 +72,27 @@ namespace brep
}
};
+ class page
+ {
+ web::xhtml::fragment logo
+ {
+ "<xhtml>",
+ "Web page logo. It is displayed in the page header aligned to the left
+ edge. The value is treated as an XHTML5 fragment."
+ }
+
+ vector<page_menu> menu;
+ {
+ "<label=link>",
+ "Web page menu. Each entry is displayed in the page header in the order
+ specified and aligned to the right edge. A link target that starts
+ with \cb{/} or contains \cb{:} is used as is. Otherwise, it is prefixed
+ with the repository web interface root."
+
+ // @@ Might need updating.
+ }
+ };
+
class search
{
uint16_t search-results = 10
@@ -104,19 +127,19 @@ namespace brep
// Module options.
//
- class package_search: search, db, module
+ class package_search: search, db, page, module
{
};
- class package_details: package, search, db, module
+ class package_details: package, search, db, page, module
{
};
- class package_version_details: package, db, module
+ class package_version_details: package, db, page, module
{
};
- class repository_details: db, module
+ class repository_details: db, page, module
{
};
diff --git a/brep/page b/brep/page
index 5836ea6..4f97ef8 100644
--- a/brep/page
+++ b/brep/page
@@ -7,10 +7,13 @@
#include <xml/forward>
+#include <web/xhtml-fragment>
+
#include <brep/types>
#include <brep/utility>
#include <brep/package>
+#include <brep/options-types> // page_menu
namespace brep
{
@@ -25,7 +28,7 @@ namespace brep
CSS_LINKS (const path& p, const dir_path& r): path_ (p), root_ (r) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const path& path_;
@@ -37,13 +40,18 @@ namespace brep
class DIV_HEADER
{
public:
- DIV_HEADER (const dir_path& r): root_ (r) {}
+ DIV_HEADER (const dir_path& root,
+ const web::xhtml::fragment& logo,
+ const vector<page_menu>& menu):
+ root_ (root), logo_ (logo), menu_ (menu) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const dir_path& root_;
+ const web::xhtml::fragment& logo_;
+ const vector<page_menu>& menu_;
};
// Generates package search form element.
@@ -54,7 +62,7 @@ namespace brep
FORM_SEARCH (const string& q): query_ (q) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const string& query_;
@@ -73,7 +81,7 @@ namespace brep
: count_ (c), singular_ (s), plural_ (p) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
size_t count_;
@@ -90,7 +98,7 @@ namespace brep
: name_ (n), query_param_ (q), root_ (r) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const string& name_;
@@ -114,7 +122,7 @@ namespace brep
: package_ (nullptr), version_ (v), root_ (nullptr) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const string* package_;
@@ -130,7 +138,7 @@ namespace brep
TR_SUMMARY (const string& s): summary_ (s) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const string& summary_;
@@ -144,7 +152,7 @@ namespace brep
TR_LICENSE (const license_alternatives& l): licenses_ (l) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const license_alternatives& licenses_;
@@ -159,7 +167,7 @@ namespace brep
TR_LICENSES (const license_alternatives& l): licenses_ (l) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const license_alternatives& licenses_;
@@ -173,7 +181,7 @@ namespace brep
TR_TAGS (const strings& ts, const dir_path& r): tags_ (ts), root_ (r) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const strings& tags_;
@@ -189,7 +197,7 @@ namespace brep
: dependencies_ (d), root_ (r) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const dependencies& dependencies_;
@@ -204,7 +212,7 @@ namespace brep
TR_REQUIRES (const requirements& r): requirements_ (r) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const requirements& requirements_;
@@ -218,7 +226,7 @@ namespace brep
TR_URL (const url& u, const char* l = "url"): url_ (u), label_ (l) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const url& url_;
@@ -234,7 +242,7 @@ namespace brep
: email_ (e), label_ (l) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const email& email_;
@@ -249,7 +257,7 @@ namespace brep
TR_PRIORITY (const priority& p): priority_ (p) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const priority& priority_;
@@ -264,7 +272,7 @@ namespace brep
: name_ (n), root_ (r) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const string& name_;
@@ -279,7 +287,7 @@ namespace brep
TR_DOWNLOAD (const string& u): url_ (u) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const string& url_;
@@ -293,7 +301,7 @@ namespace brep
SPAN_COMMENT (const string& c): comment_ (c) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const string& comment_;
@@ -315,7 +323,7 @@ namespace brep
: description_ (d), length_ (l), url_ (&u) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const string& description_;
@@ -340,7 +348,7 @@ namespace brep
: changes_ (c), length_ (l), url_ (&u) {}
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
const string& changes_;
@@ -360,7 +368,7 @@ namespace brep
const string& url);
void
- operator() (xml::serializer& s) const;
+ operator() (xml::serializer&) const;
private:
size_t current_page_;
diff --git a/brep/page.cxx b/brep/page.cxx
index a6c7da2..27e1b2a 100644
--- a/brep/page.cxx
+++ b/brep/page.cxx
@@ -40,18 +40,35 @@ namespace brep
void DIV_HEADER::
operator() (serializer& s) const
{
- s << DIV(ID="header-bar")
- << DIV(ID="header")
- << DIV(ID="header-logo")
- << ~DIV
- << DIV(ID="header-menu")
- << DIV(ID="header-menu-body")
- << A(HREF=root_) << "Packages" << ~A
- << A(HREF=root_.string () + "?about") << "About" << ~A
- << ~DIV
- << ~DIV
- << ~DIV
- << ~DIV;
+ if (!logo_.empty () || !menu_.empty ())
+ {
+ s << DIV(ID="header-bar")
+ << DIV(ID="header");
+
+ if (!logo_.empty ())
+ s << DIV(ID="header-logo") << logo_ << ~DIV;
+
+ if (!menu_.empty ())
+ {
+ s << DIV(ID="header-menu")
+ << DIV(ID="header-menu-body");
+
+ for (const auto& m: menu_)
+ {
+ const string& l (m.link[0] == '/' || m.link.find (':') != string::npos
+ ? m.link
+ : root_.string () + m.link);
+
+ s << A(HREF=l) << m.label << ~A;
+ }
+
+ s << ~DIV
+ << ~DIV;
+ }
+
+ s << ~DIV
+ << ~DIV;
+ }
}
// FORM_SEARCH
diff --git a/brep/types-parsers b/brep/types-parsers
index 8401237..78b7088 100644
--- a/brep/types-parsers
+++ b/brep/types-parsers
@@ -8,6 +8,8 @@
#ifndef BREP_TYPES_PARSERS
#define BREP_TYPES_PARSERS
+#include <web/xhtml-fragment>
+
#include <brep/types>
#include <brep/utility>
@@ -35,6 +37,20 @@ namespace brep
static void
parse (page_form&, scanner&);
};
+
+ template <>
+ struct parser<page_menu>
+ {
+ static void
+ parse (page_menu&, scanner&);
+ };
+
+ template <>
+ struct parser<web::xhtml::fragment>
+ {
+ static void
+ parse (web::xhtml::fragment&, scanner&);
+ };
}
}
diff --git a/brep/types-parsers.cxx b/brep/types-parsers.cxx
index d46ea18..67f4812 100644
--- a/brep/types-parsers.cxx
+++ b/brep/types-parsers.cxx
@@ -7,6 +7,7 @@
#include <brep/options>
using namespace std;
+using namespace web::xhtml;
namespace brep
{
@@ -59,5 +60,55 @@ namespace brep
else
throw invalid_value (o, v);
}
+
+ // Parse page_menu.
+ //
+ void parser<page_menu>::
+ parse (page_menu& x, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (!s.more ())
+ throw missing_value (o);
+
+ const string v (s.next ());
+
+ auto p (v.find ('='));
+ if (p != string::npos)
+ {
+ string label (v, 0, p);
+ string link (v, p + 1);
+
+ if (!label.empty ())
+ {
+ x = page_menu (move (label), move (link));
+ return;
+ }
+ }
+
+ throw invalid_value (o, v);
+ }
+
+ // Parse web::xhtml::fragment.
+ //
+ void parser<fragment>::
+ parse (fragment& x, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (!s.more ())
+ throw missing_value (o);
+
+ const char* v (s.next ());
+
+ try
+ {
+ x = fragment (v, o);
+ }
+ catch (const xml::parsing&)
+ {
+ throw invalid_value (o, v);
+ }
+ }
}
}
diff --git a/etc/brep-module.conf b/etc/brep-module.conf
index 26767d6..8525135 100644
--- a/etc/brep-module.conf
+++ b/etc/brep-module.conf
@@ -4,6 +4,20 @@
# out options indicate their default values.
#
+# Web page logo. It is displayed in the page header aligned to the left edge.
+# The value is treated as an XHTML5 fragment.
+#
+# logo ""
+
+# Web page menu. Each entry is displayed in the page header in the order
+# specified and aligned to the right edge. A link target that starts with '/' or
+# contains ':' is used as is. Otherwise, it is prefixed with the repository web
+# interface root.
+#
+menu Packages=
+menu About=?about
+
+
# Number of results per page.
#
# search-results 10
diff --git a/web/xhtml-fragment b/web/xhtml-fragment
new file mode 100644
index 0000000..a0964b0
--- /dev/null
+++ b/web/xhtml-fragment
@@ -0,0 +1,48 @@
+// file : web/xhtml-fragment -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef WEB_XHTML_FRAGMENT
+#define WEB_XHTML_FRAGMENT
+
+#include <string>
+#include <vector>
+#include <utility> // pair
+
+#include <xml/parser>
+#include <xml/forward>
+
+namespace web
+{
+ namespace xhtml
+ {
+ // A parsed (via xml::parser) XHTML fragment that can later be serialized
+ // to xml::serializer.
+ //
+ class fragment
+ {
+ public:
+ fragment () = default;
+
+ // Parse string as XHTML document fragment. The fragment should be
+ // complete, in the sense that all elements should have closing tags.
+ // Elements and attributes are considered to be in the namespace of the
+ // entire XHTML document, so no namespace should be specified for them.
+ // Do not validate against XHTML vocabulary. Can throw xml::parsing
+ // exception.
+ //
+ fragment (const std::string& xhtml, const std::string& input_name);
+
+ void
+ operator() (xml::serializer&) const;
+
+ bool
+ empty () const {return events_.empty ();}
+
+ private:
+ std::vector<std::pair<xml::parser::event_type, std::string>> events_;
+ };
+ }
+}
+
+#endif // WEB_XHTML_FRAGMENT
diff --git a/web/xhtml-fragment.cxx b/web/xhtml-fragment.cxx
new file mode 100644
index 0000000..34247dd
--- /dev/null
+++ b/web/xhtml-fragment.cxx
@@ -0,0 +1,115 @@
+// file : web/xhtml-fragment.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <web/xhtml-fragment>
+
+#include <string>
+#include <cassert>
+
+#include <xml/parser>
+#include <xml/serializer>
+
+#include <web/xhtml>
+
+using namespace std;
+using namespace xml;
+
+namespace web
+{
+ namespace xhtml
+ {
+ fragment::
+ fragment (const string& text, const string& name)
+ {
+ // To parse the fragment make it a valid xml document, wrapping with the
+ // root element.
+ //
+ string doc ("<d>" + text + "</d>");
+
+ parser p (
+ doc.c_str (),
+ doc.size (),
+ name,
+ parser::receive_elements | parser::receive_characters |
+ parser::receive_attributes_event);
+
+ for (parser::event_type e: p)
+ {
+ switch (e)
+ {
+ case parser::start_element:
+ case parser::start_attribute:
+ {
+ const auto& n (p.qname ());
+
+ if (!n.namespace_ ().empty ())
+ throw parsing (
+ name, p.line (), p.column (), "namespace is not allowed");
+
+ events_.emplace_back (e, n.name ());
+ break;
+ }
+ case parser::end_element:
+ case parser::end_attribute:
+ {
+ events_.emplace_back (e, "");
+ break;
+ }
+ case parser::characters:
+ {
+ events_.emplace_back (e, p.value ());
+ break;
+ }
+ default:
+ assert (false);
+ }
+ }
+
+ // Unwrap the fragment removing the root element events.
+ //
+ assert (events_.size () >= 2);
+ events_.erase (events_.begin ());
+ events_.pop_back ();
+ }
+
+ void fragment::
+ operator() (serializer& s) const
+ {
+ for (const auto& e: events_)
+ {
+ switch (e.first)
+ {
+ case parser::start_element:
+ {
+ s.start_element (xmlns, e.second);
+ break;
+ }
+
+ case parser::start_attribute:
+ {
+ s.start_attribute (e.second);
+ break;
+ }
+ case parser::end_element:
+ {
+ s.end_element ();
+ break;
+ }
+ case parser::end_attribute:
+ {
+ s.end_attribute ();
+ break;
+ }
+ case parser::characters:
+ {
+ s.characters (e.second);
+ break;
+ }
+ default:
+ assert (false);
+ }
+ }
+ }
+ }
+}