aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-11-16 12:08:41 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-11-16 16:42:40 +0200
commit6eca8a647c79e9a5b100672b55f5d02273a28772 (patch)
tree5361b9007cace2dfdf95c55b009abdcaab1ad60e
parentba283a252267e0365408db3d6b7cf410edccac1b (diff)
Implement 'about' web page
-rw-r--r--brep/buildfile4
-rw-r--r--brep/options.cli4
-rw-r--r--brep/package-details.cxx5
-rw-r--r--brep/package-search.cxx6
-rw-r--r--brep/package-version-details.cxx7
-rw-r--r--brep/page22
-rw-r--r--brep/page.cxx65
-rw-r--r--brep/repository-details32
-rw-r--r--brep/repository-details.cxx96
-rw-r--r--brep/services.cxx7
-rwxr-xr-xbuild.sh4
-rw-r--r--etc/httpd.conf12
-rw-r--r--tests/loader/driver.cxx20
-rw-r--r--tests/loader/internal/1/math/repositories6
-rw-r--r--tests/loader/internal/1/stable/repositories3
-rw-r--r--web/mime-url-encoding.cxx2
-rw-r--r--www/repository-details.css27
17 files changed, 293 insertions, 29 deletions
diff --git a/brep/buildfile b/brep/buildfile
index 6676095..464118d 100644
--- a/brep/buildfile
+++ b/brep/buildfile
@@ -22,8 +22,8 @@ libso{brep}: cxx.export.poptions = -I$out_root -I$src_root
import libs += libstudxml%lib{studxml}
brep = cxx{diagnostics module services package-search package-details \
- package-version-details shared-database page types-parsers} \
- cli.cxx{options}
+ package-version-details repository-details shared-database page \
+ types-parsers} cli.cxx{options}
web = ../web/apache/cxx{request service} ../web/cxx{mime-url-encoding}
diff --git a/brep/options.cli b/brep/options.cli
index 6c3c7f1..77dc45a 100644
--- a/brep/options.cli
+++ b/brep/options.cli
@@ -43,6 +43,10 @@ namespace brep
std::uint16_t description-length = 400;
std::uint16_t changes-length = 800;
};
+
+ class repository_details: module, db
+ {
+ };
}
// Web module HTTP request parameters.
diff --git a/brep/package-details.cxx b/brep/package-details.cxx
index ef787b0..780d98a 100644
--- a/brep/package-details.cxx
+++ b/brep/package-details.cxx
@@ -61,7 +61,6 @@ namespace brep
void package_details::
handle (request& rq, response& rs)
{
- using namespace xml;
using namespace web;
using namespace web::xhtml;
@@ -108,7 +107,7 @@ namespace brep
return u;
});
- serializer s (rs.content (), name);
+ xml::serializer s (rs.content (), name);
const string& title (sq.empty () ? name : name + " " + sq);
static const path sp ("package-details.css");
@@ -219,7 +218,7 @@ namespace brep
//
// Hm, I am not so sure about this. Consider: stable/testing/unstable.
//
- s << TR_LOCATION (p->internal_repository.object_id ())
+ s << TR_LOCATION (p->internal_repository.object_id (), rt)
<< TR_DEPENDS (p->dependencies, rt)
<< TR_REQUIRES (p->requirements)
<< ~TBODY
diff --git a/brep/package-search.cxx b/brep/package-search.cxx
index b0352f3..1617e58 100644
--- a/brep/package-search.cxx
+++ b/brep/package-search.cxx
@@ -57,8 +57,6 @@ namespace brep
void package_search::
handle (request& rq, response& rs)
{
- using namespace xml;
- using namespace web;
using namespace web::xhtml;
MODULE_DIAG;
@@ -82,10 +80,10 @@ namespace brep
}
const string& sq (pr.query ()); // Search query.
- string qp (sq.empty () ? "" : "q=" + mime_url_encode (sq));
+ string qp (sq.empty () ? "" : "q=" + web::mime_url_encode (sq));
size_t pg (pr.page ());
- serializer s (rs.content (), "Packages");
+ xml::serializer s (rs.content (), "Packages");
const string& title (
sq.empty () ? s.output_name () : s.output_name () + " " + sq);
diff --git a/brep/package-version-details.cxx b/brep/package-version-details.cxx
index b8f30ff..d00c10a 100644
--- a/brep/package-version-details.cxx
+++ b/brep/package-version-details.cxx
@@ -46,7 +46,6 @@ namespace brep
void package_version_details::
handle (request& rq, response& rs)
{
- using namespace xml;
using namespace web;
using namespace web::xhtml;
@@ -99,7 +98,7 @@ namespace brep
return u;
});
- serializer s (rs.content (), name);
+ xml::serializer s (rs.content (), name);
static const path go ("go");
static const path sp ("package-version-details.css");
@@ -171,7 +170,7 @@ namespace brep
<< TR_PRIORITY (p->priority)
<< TR_LICENSES (p->license_alternatives)
- << TR_LOCATION (p->internal_repository.object_id ())
+ << TR_LOCATION (p->internal_repository.object_id (), rt)
<< TR_DOWNLOAD (du)
<< ~TBODY
<< ~TABLE
@@ -249,7 +248,7 @@ namespace brep
s << ' ' << A(HREF=u / path (p->version.string ())) << *dc << ~A;
}
else
- // Display the dependency as a plain text in no repository URL
+ // Display the dependency as a plain text if no repository URL
// available.
//
s << d;
diff --git a/brep/page b/brep/page
index c968389..0d0887f 100644
--- a/brep/page
+++ b/brep/page
@@ -22,8 +22,7 @@ namespace brep
class CSS_LINKS
{
public:
- CSS_LINKS (const path& p, const dir_path& r):
- path_ (p), root_ (r) {}
+ CSS_LINKS (const path& p, const dir_path& r): path_ (p), root_ (r) {}
void
operator() (xml::serializer& s) const;
@@ -263,13 +262,15 @@ namespace brep
class TR_LOCATION
{
public:
- TR_LOCATION (const std::string& l): location_ (l) {}
+ TR_LOCATION (const std::string& n, const dir_path& r)
+ : name_ (n), root_ (r) {}
void
operator() (xml::serializer& s) const;
private:
- const std::string& location_;
+ const std::string& name_;
+ const dir_path& root_;
};
// Generates package download URL element.
@@ -307,13 +308,13 @@ namespace brep
public:
// Genereate full description.
//
- P_DESCRIPTION (const std::string& d)
- : description_ (d), length_ (d.size ()), url_ (nullptr) {}
+ P_DESCRIPTION (const std::string& d, bool u = true)
+ : description_ (d), length_ (d.size ()), url_ (nullptr), unique_ (u) {}
// Genereate brief description.
//
P_DESCRIPTION (const std::string& d, size_t l, const std::string& u)
- : description_ (d), length_ (l), url_ (&u) {}
+ : description_ (d), length_ (l), url_ (&u), unique_ (false) {}
void
operator() (xml::serializer& s) const;
@@ -322,6 +323,7 @@ namespace brep
const std::string& description_;
std::size_t length_;
const std::string* url_; // Full page url.
+ bool unique_;
};
// Generates package description element.
@@ -369,6 +371,12 @@ namespace brep
std::size_t page_number_count_;
const std::string& url_;
};
+
+ // Convert the argument to a string representing the valid HTML 5 'id'
+ // attribute value.
+ //
+ std::string
+ id_attribute (const std::string& v);
}
#endif // BREP_PAGE
diff --git a/brep/page.cxx b/brep/page.cxx
index fbbcda8..5b650a3 100644
--- a/brep/page.cxx
+++ b/brep/page.cxx
@@ -5,10 +5,13 @@
#include <brep/page>
#include <set>
+#include <ios> // hex, uppercase, right
#include <string>
#include <memory> // shared_ptr
#include <cstddef> // size_t
#include <cassert>
+#include <sstream>
+#include <iomanip> // setw(), setfill()
#include <algorithm> // min()
#include <xml/serializer>
@@ -27,6 +30,7 @@ using namespace web::xhtml;
namespace brep
{
static const path go ("go");
+ static const path about ("about");
// CSS_LINKS
//
@@ -44,12 +48,10 @@ namespace brep
void DIV_HEADER::
operator() (serializer& s) const
{
- static const path a ("about");
-
s << DIV(ID="header")
<< DIV(ID="header-menu")
<< A(HREF=root_) << "packages" << ~A
- << A(HREF=root_ / a) << "about" << ~A
+ << A(HREF=root_ / about) << "about" << ~A
<< ~DIV
<< ~DIV;
}
@@ -305,7 +307,7 @@ namespace brep
else if (p->internal ())
s << A(HREF=root_ / go / path (en)) << n << ~A;
else
- // Display the dependency as a plain text in no repository URL
+ // Display the dependency as a plain text if no repository URL
// available.
//
s << n;
@@ -440,7 +442,16 @@ namespace brep
{
s << TR(CLASS="location")
<< TH << "location" << ~TH
- << TD << SPAN(CLASS="value") << location_ << ~SPAN << ~TD
+ << TD
+ << SPAN(CLASS="value")
+ << A
+ << HREF
+ << root_ / about << "#" << mime_url_encode (id_attribute (name_))
+ << ~HREF
+ << name_
+ << ~A
+ << ~SPAN
+ << ~TD
<< ~TR;
}
@@ -486,7 +497,10 @@ namespace brep
// Format the description into paragraphs, recognizing a blank line as
// paragraph separator, and replacing single newlines with a space.
//
- s << P(ID="description");
+ s << P;
+
+ if (unique_)
+ s << ID("description");
bool nl (false); // The previous character is '\n'.
for (const auto& c: d)
@@ -614,4 +628,43 @@ namespace brep
s << ~DIV;
}
}
+
+ // Convert the argument to a string conformant to the section
+ // "3.2.5.1 The id attribute" of the HTML 5 specification at
+ // http://www.w3.org/TR/html5/dom.html#the-id-attribute.
+ //
+ string
+ id_attribute (const string& v)
+ {
+ ostringstream o;
+ o << hex << uppercase << right << setfill ('0');
+
+ // Replace space characters (as specified at
+ // http://www.w3.org/TR/html5/infrastructure.html#space-character) with
+ // the respective escape sequences.
+ //
+ for (auto c: v)
+ {
+ switch (c)
+ {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\f':
+ case '~':
+ {
+ // Intentionally use '~' as an escape character to leave it unescaped
+ // being a part of URL. For example
+ // http://cppget.org/about#cppget.org%2Fmath~20lab
+ //
+ o << "~" << setw (2) << static_cast<unsigned short> (c);
+ break;
+ }
+ default: o << c; break;
+ }
+ }
+
+ return o.str ();
+ }
}
diff --git a/brep/repository-details b/brep/repository-details
new file mode 100644
index 0000000..a3008dc
--- /dev/null
+++ b/brep/repository-details
@@ -0,0 +1,32 @@
+// file : brep/repository-details -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BREP_REPOSITORY_DETAILS
+#define BREP_REPOSITORY_DETAILS
+
+#include <memory> // shared_ptr
+
+#include <odb/forward.hxx> // database
+
+#include <brep/module>
+#include <brep/options>
+
+namespace brep
+{
+ class repository_details: public module
+ {
+ private:
+ virtual void
+ handle (request&, response&);
+
+ virtual void
+ init (cli::scanner&);
+
+ private:
+ std::shared_ptr<options::repository_details> options_;
+ std::shared_ptr<odb::core::database> db_;
+ };
+}
+
+#endif // BREP_REPOSITORY_DETAILS
diff --git a/brep/repository-details.cxx b/brep/repository-details.cxx
new file mode 100644
index 0000000..4302c15
--- /dev/null
+++ b/brep/repository-details.cxx
@@ -0,0 +1,96 @@
+// file : brep/repository-details.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <brep/repository-details>
+
+#include <string>
+#include <memory> // make_shared()
+
+#include <xml/serializer>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <web/xhtml>
+#include <web/module>
+#include <web/mime-url-encoding>
+
+#include <brep/page>
+#include <brep/options>
+#include <brep/package>
+#include <brep/package-odb>
+#include <brep/shared-database>
+
+using namespace std;
+using namespace odb::core;
+
+namespace brep
+{
+ using namespace cli;
+
+ void repository_details::
+ init (scanner& s)
+ {
+ MODULE_DIAG;
+
+ options_ = make_shared<options::repository_details> (
+ s, unknown_mode::fail, unknown_mode::fail);
+
+ db_ = shared_database (options_->db_host (), options_->db_port ());
+ }
+
+ void repository_details::
+ handle (request&, response& rs)
+ {
+ using namespace web::xhtml;
+
+ MODULE_DIAG;
+
+ // The module options object is not changed after being created once per
+ // server process.
+ //
+ static const dir_path& rt (options_->root ());
+
+ xml::serializer s (rs.content (), "About");
+ const string& title (s.output_name ());
+ static const path sp ("repository-details.css");
+
+ s << HTML
+ << HEAD
+ << TITLE << title << ~TITLE
+ << CSS_LINKS (sp, rt)
+ << ~HEAD
+ << BODY
+ << DIV_HEADER (rt)
+ << DIV(ID="content");
+
+ transaction t (db_->begin ());
+
+ using query = query<repository>;
+ auto rp (db_->query<repository> (query::internal + "ORDER BY name"));
+
+ for (const auto& r: rp)
+ {
+ string id (id_attribute (r.name));
+ s << H1(ID=id)
+ << A(HREF="#" + web::mime_url_encode (id)) << r.name << ~A
+ << ~H1;
+
+ if (r.email)
+ s << A << HREF << "mailto:" << *r.email << ~HREF << *r.email << ~A;
+
+ if (r.summary)
+ s << H2 << *r.summary << ~H2;
+
+ if (r.description)
+ s << P_DESCRIPTION (*r.description, false);
+ }
+
+ t.commit ();
+
+ s << ~DIV
+ << ~BODY
+ << ~HTML;
+ }
+}
diff --git a/brep/services.cxx b/brep/services.cxx
index 9b7043c..e41d3f3 100644
--- a/brep/services.cxx
+++ b/brep/services.cxx
@@ -8,6 +8,7 @@
#include <brep/package-search>
#include <brep/package-details>
+#include <brep/repository-details>
#include <brep/package-version-details>
using namespace brep;
@@ -30,3 +31,9 @@ service AP_MODULE_DECLARE_DATA package_version_details_srv (
"package-version-details",
package_version_details_mod,
{"root", "db-host", "db-port", "conf"});
+
+static repository_details repository_details_mod;
+service AP_MODULE_DECLARE_DATA repository_details_srv (
+ "repository-details",
+ repository_details_mod,
+ {"root", "db-host", "db-port", "conf"});
diff --git a/build.sh b/build.sh
index 9596589..0a7fb34 100755
--- a/build.sh
+++ b/build.sh
@@ -34,8 +34,8 @@ cli --include-with-brackets --include-prefix brep --hxx-suffix "" \
echo "g++ libbrep-apache.so"
s="package-search.cxx package-details.cxx package-version-details.cxx \
-module.cxx diagnostics.cxx page.cxx services.cxx options.cxx \
-shared-database.cxx \
+repository-details.cxx module.cxx diagnostics.cxx page.cxx services.cxx \
+options.cxx shared-database.cxx \
../web/apache/request.cxx ../web/apache/service.cxx \
../web/mime-url-encoding.cxx"
diff --git a/etc/httpd.conf b/etc/httpd.conf
index c8405ed..7c25a6d 100644
--- a/etc/httpd.conf
+++ b/etc/httpd.conf
@@ -67,6 +67,14 @@ LoadModule package_version_details_srv ${AP_MODULE_DIR}/libbrep-apache.so
package-version-details-conf ${AP_CONFIG_DIR}/package-version-details.conf
</IfModule>
+LoadModule repository_details_srv ${AP_MODULE_DIR}/libbrep-apache.so
+
+<IfModule repository_details_srv>
+ repository-details-root ${AP_ROOT}/
+ repository-details-db-host ${AP_DB_HOST}
+ repository-details-db-port ${AP_DB_PORT}
+</IfModule>
+
<LocationMatch ^${AP_ROOT}/?$>
SetHandler package-search
</LocationMatch>
@@ -79,6 +87,10 @@ LoadModule package_version_details_srv ${AP_MODULE_DIR}/libbrep-apache.so
SetHandler package-version-details
</LocationMatch>
+<LocationMatch ^${AP_ROOT}/about$>
+ SetHandler repository-details
+</LocationMatch>
+
AliasMatch ^${AP_ROOT}/(.+) ${AP_WWW_DIR}/$1
ExtendedStatus On
diff --git a/tests/loader/driver.cxx b/tests/loader/driver.cxx
index cf81dca..0d361ab 100644
--- a/tests/loader/driver.cxx
+++ b/tests/loader/driver.cxx
@@ -118,6 +118,12 @@ main (int argc, char* argv[])
"http://pkg.cppget.org/internal/1/stable");
assert (sr->display_name == "stable");
assert (!sr->url);
+ assert (sr->email && *sr->email == "repoman@cppget.org");
+ assert (sr->summary &&
+ *sr->summary == "General C++ package stable repository");
+ assert (sr->description && *sr->description ==
+ "This is the awesome C++ package repository full of exciting "
+ "stuff.");
dir_path srp (cp.directory () / dir_path ("internal/1/stable"));
assert (sr->local_path == srp.normalize ());
@@ -283,6 +289,11 @@ main (int argc, char* argv[])
"http://pkg.cppget.org/internal/1/math");
assert (mr->display_name == "math");
assert (!mr->url);
+ assert (mr->email && *mr->email == "repoman@cppget.org");
+ assert (mr->summary && *mr->summary == "Math C++ package repository");
+ assert (mr->description && *mr->description ==
+ "This is the awesome C++ package repository full of remarkable "
+ "algorithms and\nAPIs.");
dir_path mrp (cp.directory () / dir_path ("internal/1/math"));
assert (mr->local_path == mrp.normalize ());
@@ -480,6 +491,9 @@ main (int argc, char* argv[])
"http://pkg.cppget.org/external/1/misc");
assert (cr->display_name.empty ());
assert (cr->url && *cr->url == "http://misc.cppget.org/");
+ assert (!cr->email);
+ assert (!cr->summary);
+ assert (!cr->description);
dir_path crp (cp.directory () / dir_path ("external/1/misc"));
assert (cr->local_path == crp.normalize ());
@@ -535,6 +549,9 @@ main (int argc, char* argv[])
"http://pkg.cppget.org/external/1/testing");
assert (tr->display_name.empty ());
assert (tr->url && *tr->url == "http://test.cppget.org/hello/");
+ assert (!tr->email);
+ assert (!tr->summary);
+ assert (!tr->description);
dir_path trp (cp.directory () / dir_path ("external/1/testing"));
assert (tr->local_path == trp.normalize ());
@@ -568,6 +585,9 @@ main (int argc, char* argv[])
"http://pkg.cppget.org/external/1/staging");
assert (gr->display_name.empty ());
assert (gr->url && *gr->url == "http://stage.cppget.org/");
+ assert (!gr->email);
+ assert (!gr->summary);
+ assert (!gr->description);
dir_path grp (cp.directory () / dir_path ("external/1/staging"));
assert (gr->local_path == grp.normalize ());
diff --git a/tests/loader/internal/1/math/repositories b/tests/loader/internal/1/math/repositories
index 814d1db..c2df027 100644
--- a/tests/loader/internal/1/math/repositories
+++ b/tests/loader/internal/1/math/repositories
@@ -5,3 +5,9 @@ location: ../../../external/1/misc
:
# Local repository manifest (this repository).
#
+email: repoman@cppget.org
+summary: Math C++ package repository
+description: \
+This is the awesome C++ package repository full of remarkable algorithms and
+APIs.
+\
diff --git a/tests/loader/internal/1/stable/repositories b/tests/loader/internal/1/stable/repositories
index 38fdd72..c06f731 100644
--- a/tests/loader/internal/1/stable/repositories
+++ b/tests/loader/internal/1/stable/repositories
@@ -9,3 +9,6 @@ location: ../math
:
# Local repository manifest (this repository).
#
+email: repoman@cppget.org
+summary: General C++ package stable repository
+description: This is the awesome C++ package repository full of exciting stuff.
diff --git a/web/mime-url-encoding.cxx b/web/mime-url-encoding.cxx
index 1d68bf3..04c5a63 100644
--- a/web/mime-url-encoding.cxx
+++ b/web/mime-url-encoding.cxx
@@ -6,7 +6,7 @@
#include <ios> // hex, uppercase, right
#include <string>
-#include <iomanip> // setw(), setfill ()
+#include <iomanip> // setw(), setfill()
#include <ostream>
#include <sstream>
#include <cstring> // size_t, strspn()
diff --git a/www/repository-details.css b/www/repository-details.css
new file mode 100644
index 0000000..3a57b8d
--- /dev/null
+++ b/www/repository-details.css
@@ -0,0 +1,27 @@
+h1
+{
+ font-family: monospace;
+ font-weight: normal;
+ font-size: 2.074em;
+ line-height: 1.4em;
+
+ margin: .6em 0 .2em 0;
+
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+/* Since it is a link to itself, it will always be visited. */
+h1 a:visited {color: #006fbf;}
+h1 a:hover, h1 a:active {color: #0087e7; text-decoration: none;}
+
+h2
+{
+ font-style: italic;
+ font-weight: normal;
+ font-size: 1.32em;
+ line-height: 1.4em;
+
+ margin: .4em 0 .4em 0;
+}