aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--brep/buildfile2
-rw-r--r--brep/options.cli1
-rw-r--r--brep/package7
-rw-r--r--brep/package-version-search.cxx162
-rw-r--r--brep/page52
-rw-r--r--brep/page.cxx103
-rwxr-xr-xbuild.sh2
-rw-r--r--etc/package-version-search.conf3
-rw-r--r--tests/loader/driver.cxx95
-rw-r--r--tests/loader/internal/1/math/packages10
-rw-r--r--tests/loader/internal/1/stable/packages8
-rw-r--r--web/xhtml37
12 files changed, 445 insertions, 37 deletions
diff --git a/brep/buildfile b/brep/buildfile
index 9b64ed4..7e73d2b 100644
--- a/brep/buildfile
+++ b/brep/buildfile
@@ -22,7 +22,7 @@ libso{brep}: cxx.export.poptions = -I$out_root -I$src_root
import libs += libstudxml%lib{studxml}
brep = cxx{diagnostics module services package-search package-version-search \
- shared-database} cli.cxx{options}
+ shared-database page} cli.cxx{options}
web = ../web/apache/cxx{request service} ../web/cxx{mime-url-encoding}
libso{brep-apache}: $brep $web libso{brep} $libs
diff --git a/brep/options.cli b/brep/options.cli
index 8062f59..a042f45 100644
--- a/brep/options.cli
+++ b/brep/options.cli
@@ -30,6 +30,7 @@ namespace brep
class package_version_search: module, db
{
std::uint16_t results-on-page = 10;
+ std::uint16_t pages-in-pager = 10;
};
}
diff --git a/brep/package b/brep/package
index 0132b73..0ef4407 100644
--- a/brep/package
+++ b/brep/package
@@ -482,6 +482,13 @@ namespace brep
#pragma db member(id) virtual(package_version::_id_type) \
get() set(_id (std::move (?)))
};
+
+ #pragma db view object(package_version)
+ struct package_version_count
+ {
+ #pragma db column("count(1)")
+ std::size_t count;
+ };
}
// Nested container emulation support for ODB.
diff --git a/brep/package-version-search.cxx b/brep/package-version-search.cxx
index a989434..d95b392 100644
--- a/brep/package-version-search.cxx
+++ b/brep/package-version-search.cxx
@@ -16,6 +16,7 @@
#include <web/module>
#include <web/mime-url-encoding>
+#include <brep/page>
#include <brep/package>
#include <brep/package-odb>
#include <brep/shared-database>
@@ -60,14 +61,171 @@ namespace brep
throw invalid_request (400, e.what ());
}
- const char* title ("Package");
+ const char* ident ("\n ");
+ const string title ("Package " + name);
serializer s (rs.content (), title);
s << HTML
<< HEAD
<< TITLE << title << ~TITLE
+ << CSS_STYLE << ident
+ << pager_style () << ident
+ << "a {text-decoration: none;}" << ident
+ << "a:hover {text-decoration: underline;}" << ident
+ << ".name {font-size: xx-large; font-weight: bold;}" << ident
+ << ".summary {font-size: x-large; margin: 0.2em 0 0;}" << ident
+ << ".url {font-size: small;}" << ident
+ << ".email {font-size: small;}" << ident
+ << ".description {margin: 0.5em 0 0;}" << ident
+ << ".tags {margin: 0.5em 0 0;}" << ident
+ << ".tag {padding: 0 0.3em 0 0;}" << ident
+ << ".versions {font-size: x-large; margin: 0.5em 0 0;}" << ident
+ << ".package_version {margin: 0.5em 0 0;}" << ident
+ << ".version {font-size: x-large;}" << ident
+ << ~CSS_STYLE
<< ~HEAD
- << BODY << name << ~BODY
+ << BODY;
+
+ transaction t (db_->begin ());
+
+ shared_ptr<package> p;
+
+ try
+ {
+ p = db_->load<package> (name);
+ }
+ catch (const object_not_persistent& )
+ {
+ throw invalid_request (404, "Package '" + name + "' not found");
+ }
+
+ s << DIV(CLASS="name")
+ << name
+ << ~DIV
+ << DIV(CLASS="summary")
+ << p->summary
+ << ~DIV;
+
+ s << DIV(CLASS="url")
+ << A << HREF << p->url << ~HREF << p->url << ~A
+ << ~DIV
+ << DIV(CLASS="email")
+ << A << HREF << "mailto:" << p->email << ~HREF << p->email << ~A
+ << ~DIV;
+
+ if (p->description)
+ s << DIV(CLASS="description")
+ << *p->description
+ << ~DIV;
+
+ if (!p->tags.empty ())
+ {
+ s << DIV(CLASS="tags");
+
+ for (const auto& t: p->tags)
+ s << SPAN(CLASS="tag") << t << ~SPAN << " ";
+
+ s << ~DIV;
+ }
+
+ // @@ Query will also include search criteria if specified.
+ //
+ size_t pvc (
+ db_->query_value<package_version_count> (
+ query<package_version_count>::id.data.package == name).count);
+
+ s << DIV(CLASS="versions")
+ << "Versions (" << pvc << ")"
+ << ~DIV;
+
+ if (p->package_url)
+ s << DIV(CLASS="url")
+ << A << HREF << *p->package_url << ~HREF << *p->package_url << ~A
+ << ~DIV;
+
+ if (p->package_email)
+ s << DIV(CLASS="email")
+ << A
+ << HREF << "mailto:" << *p->package_email << ~HREF
+ << *p->package_email
+ << ~A
+ << ~DIV;
+
+ size_t rop (options_->results_on_page ());
+
+ // @@ Use appropriate view when clarify which package version info to be
+ // displayed and search index structure get implemented. Query will also
+ // include search criteria if specified.
+ //
+ using query = query<package_version>;
+ auto r (
+ db_->query<package_version> ((query::id.data.package == name) +
+ "ORDER BY" + query::id.data.epoch + "DESC," +
+ query::id.data.canonical_upstream + "DESC," +
+ query::id.revision + "DESC " +
+ "OFFSET" + to_string (pr.page () * rop) +
+ "LIMIT" + to_string (rop)));
+
+ for (const auto& v: r)
+ {
+ static const strings priority_names (
+ {"low", "medium", "high", "security"});
+
+ assert (v.priority < priority_names.size ());
+
+ const string& vs (v.version.string ());
+
+ s << DIV(CLASS="package_version")
+ << DIV(CLASS="version")
+ << A
+ << HREF
+ << "/go/" << mime_url_encode (name) << "/" << vs << "/"
+ << ~HREF
+ << vs
+ << ~A
+ << ~DIV
+ << DIV(CLASS="priority")
+ << "Priority: " << priority_names[v.priority]
+ << ~DIV
+ << DIV(CLASS="licenses")
+ << "Licenses: ";
+
+ for (const auto& la: v.license_alternatives)
+ {
+ if (&la != &v.license_alternatives[0])
+ s << " or ";
+
+ for (const auto& l: la)
+ {
+ if (&l != &la[0])
+ s << ", ";
+
+ s << l;
+ }
+ }
+
+ s << ~DIV
+ << ~DIV;
+ }
+
+ t.commit ();
+
+ auto u (
+ [&name, &pr](size_t p)
+ {
+ string url ("/go/" + name + "/");
+ if (p > 0)
+ url += "?p=" + to_string (p);
+
+ if (!pr.query ().empty ())
+ url +=
+ string (p > 0 ? "&" : "?") + "q=" + mime_url_encode (pr.query ());
+
+ return url;
+ });
+
+ s << pager (pr.page (), pvc, rop, options_->pages_in_pager (), u)
+ << ~BODY
<< ~HTML;
}
}
diff --git a/brep/page b/brep/page
new file mode 100644
index 0000000..81b6da8
--- /dev/null
+++ b/brep/page
@@ -0,0 +1,52 @@
+// file : brep/page -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BREP_PAGE
+#define BREP_PAGE
+
+#include <string>
+#include <cstddef> // size_t
+#include <functional>
+
+#include <xml/forward>
+
+namespace brep
+{
+ // Page common building blocks.
+ //
+
+ // Generates paging element block.
+ //
+ class pager
+ {
+ public:
+ using get_url_type = std::function<std::string(std::size_t page)>;
+
+ pager (std::size_t current_page,
+ std::size_t item_count,
+ std::size_t item_per_page,
+ std::size_t page_number_count,
+ get_url_type get_url);
+
+ void
+ operator() (xml::serializer& s) const;
+
+ private:
+ std::size_t current_page_;
+ std::size_t item_count_;
+ std::size_t item_per_page_;
+ std::size_t page_number_count_;
+ get_url_type get_url_;
+ };
+
+ // Default pager element block style.
+ //
+ struct pager_style
+ {
+ void
+ operator() (xml::serializer& s) const;
+ };
+}
+
+#endif // BREP_PAGE
diff --git a/brep/page.cxx b/brep/page.cxx
new file mode 100644
index 0000000..70c6d8c
--- /dev/null
+++ b/brep/page.cxx
@@ -0,0 +1,103 @@
+// file : brep/page.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <brep/page>
+
+#include <utility> // move()
+#include <algorithm> // min()
+
+#include <xml/serializer>
+
+#include <web/xhtml>
+
+using namespace std;
+using namespace xml;
+using namespace web::xhtml;
+
+namespace brep
+{
+ // pager
+ //
+ pager::
+ pager (size_t current_page,
+ size_t item_count,
+ size_t item_per_page,
+ size_t page_number_count,
+ get_url_type get_url)
+ : current_page_ (current_page),
+ item_count_ (item_count),
+ item_per_page_ (item_per_page),
+ page_number_count_ (page_number_count),
+ get_url_ (move (get_url))
+ {
+ }
+
+ void pager::
+ operator() (serializer& s) const
+ {
+ if (item_count_ == 0 || item_per_page_ == 0)
+ return;
+
+ size_t pc (item_count_ / item_per_page_); // Page count.
+
+ if (item_count_ % item_per_page_)
+ ++pc;
+
+ if (pc > 1)
+ {
+ // Can consider customizing class names if use-case appear.
+ //
+ s << DIV(CLASS="pager");
+
+ if (current_page_ > 0)
+ s << A(CLASS="pg-prev")
+ << HREF << get_url_ (current_page_ - 1) << ~HREF
+ << "<<"
+ << ~A
+ << " ";
+
+ if (page_number_count_)
+ {
+ size_t offset (page_number_count_ / 2);
+ size_t fp (current_page_ > offset ? current_page_ - offset : 0);
+ size_t tp (min (fp + page_number_count_, pc));
+
+ for (size_t p (fp); p < tp; ++p)
+ {
+ if (p == current_page_)
+ s << SPAN(CLASS="pg-cpage")
+ << p + 1
+ << ~SPAN;
+ else
+ s << A(CLASS="pg-page")
+ << HREF << get_url_ (p) << ~HREF
+ << p + 1
+ << ~A;
+
+ s << " ";
+ }
+ }
+
+ if (current_page_ < pc - 1)
+ s << A(CLASS="pg-next")
+ << HREF << get_url_ (current_page_ + 1) << ~HREF
+ << ">>"
+ << ~A;
+
+ s << ~DIV;
+ }
+ }
+
+ // pager_style
+ //
+ void pager_style::
+ operator() (xml::serializer& s) const
+ {
+ const char* ident ("\n ");
+ s << ".pager {margin: 0.5em 0 0;}" << ident
+ << ".pg-prev {padding: 0 0.3em 0 0;}" << ident
+ << ".pg-page {padding: 0 0.3em 0 0;}" << ident
+ << ".pg-cpage {padding: 0 0.3em 0 0; font-weight: bold;}";
+ }
+}
diff --git a/build.sh b/build.sh
index ae71e72..78d68e9 100755
--- a/build.sh
+++ b/build.sh
@@ -31,7 +31,7 @@ cli --include-with-brackets --include-prefix brep --hxx-suffix "" \
echo "g++ libbrep-apache.so"
s="package-search.cxx package-version-search.cxx module.cxx diagnostics.cxx \
-services.cxx options.cxx shared-database.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/package-version-search.conf b/etc/package-version-search.conf
index aa69170..f7f0d37 100644
--- a/etc/package-version-search.conf
+++ b/etc/package-version-search.conf
@@ -8,4 +8,5 @@ verb 1
# brep::package_version_search options
#
-results-on-page 10
+results-on-page 2
+pages-in-pager 10
diff --git a/tests/loader/driver.cxx b/tests/loader/driver.cxx
index e828e79..501ae80 100644
--- a/tests/loader/driver.cxx
+++ b/tests/loader/driver.cxx
@@ -92,7 +92,7 @@ main (int argc, char* argv[])
assert (db.query<repository> ().size () == 3);
assert (db.query<package> ().size () == 4);
- assert (db.query<package_version> ().size () == 7);
+ assert (db.query<package_version> ().size () == 8);
shared_ptr<repository> sr (db.load<repository> ("cppget.org/stable"));
shared_ptr<repository> mr (db.load<repository> ("cppget.org/math"));
@@ -128,7 +128,7 @@ main (int argc, char* argv[])
sr->prerequisite_repositories[1].load ()) != pr.end ());
auto& srv (sr->package_versions);
- assert (srv.size () == 4);
+ assert (srv.size () == 5);
using lv_t = decltype (srv[0]);
auto vc ([](const lv_t& a, const lv_t& b){
@@ -145,6 +145,17 @@ main (int argc, char* argv[])
sort (srv.begin (), srv.end (), vc);
+ version fv0 ("1.0");
+ shared_ptr<package_version> fpv0 (
+ db.load<package_version> (
+ package_version_id {
+ "cppget.org/stable",
+ "libfoo",
+ fv0.epoch (),
+ fv0.canonical_upstream ()}));
+ assert (srv[0].load () == fpv0);
+ assert (check_location (fpv0));
+
version fv1 ("1.2.2");
shared_ptr<package_version> fpv1 (
db.load<package_version> (
@@ -153,7 +164,7 @@ main (int argc, char* argv[])
"libfoo",
fv1.epoch (),
fv1.canonical_upstream ()}));
- assert (srv[0].load () == fpv1);
+ assert (srv[1].load () == fpv1);
assert (check_location (fpv1));
version fv2 ("1.2.3-4");
@@ -164,7 +175,7 @@ main (int argc, char* argv[])
"libfoo",
fv2.epoch (),
fv2.canonical_upstream ()}));
- assert (srv[1].load () == fpv2);
+ assert (srv[2].load () == fpv2);
assert (check_location (fpv2));
version fv3 ("1.2.4");
@@ -175,7 +186,7 @@ main (int argc, char* argv[])
"libfoo",
fv3.epoch (),
fv3.canonical_upstream ()}));
- assert (srv[2].load () == fpv3);
+ assert (srv[3].load () == fpv3);
assert (check_location (fpv3));
version xv ("1.0.0-1");
@@ -186,7 +197,7 @@ main (int argc, char* argv[])
"libstudxml",
xv.epoch (),
xv.canonical_upstream ()}));
- assert (srv[3].load () == xpv);
+ assert (srv[4].load () == xpv);
assert (check_location (xpv));
// Verify libstudxml package.
@@ -242,24 +253,42 @@ main (int argc, char* argv[])
assert (pf->name == "libfoo");
assert (pf->summary == "The Foo Math Library");
assert (pf->tags == strings ({"c++", "foo", "math"}));
- assert (*pf->description == "Very good math library.");
+ assert (*pf->description ==
+ "A modern C++ library with easy to use linear algebra and "
+ "optimization tools. There are over 100 functions in total with "
+ "an extensive test suite. The API is similar to MATLAB.");
assert (pf->url == "http://www.example.com/foo/");
- assert (!pf->package_url);
- assert (pf->email == email ("foo-users@example.com"));
- assert (!pf->package_email);
+ assert (pf->package_url &&
+ *pf->package_url == "http://www.example.com/foo/pack");
+ assert (pf->email == "foo-users@example.com");
+ assert (pf->package_email && *pf->package_email == "pack@example.com");
auto& fpv (pf->versions);
- assert (fpv.size () == 4);
+ assert (fpv.size () == 5);
sort (fpv.begin (), fpv.end (), vc);
- assert (fpv[0].load () == fpv1);
- assert (fpv[1].load () == fpv2);
- assert (fpv[2].load () == fpv3);
+ assert (fpv[0].load () == fpv0);
+ assert (fpv[1].load () == fpv1);
+ assert (fpv[2].load () == fpv2);
+ assert (fpv[3].load () == fpv3);
// Asserting fpv[3].load () == fpv4 goes later when fpv4, belonging to
// another repository, get introduced.
//
// Verify libfoo package versions.
//
+ assert (fpv0->repository.load () == sr);
+ assert (fpv0->package.load () == pf);
+ assert (fpv0->version == version ("1.0"));
+ assert (fpv0->priority == priority::low);
+ assert (fpv0->changes.empty ());
+
+ assert (fpv0->license_alternatives.size () == 1);
+ assert (fpv0->license_alternatives[0].size () == 1);
+ assert (fpv0->license_alternatives[0][0] == "MIT");
+
+ assert (fpv0->dependencies.empty ());
+ assert (fpv0->requirements.empty ());
+
assert (fpv1->repository.load () == sr);
assert (fpv1->package.load () == pf);
assert (fpv1->version == version ("1.2.2"));
@@ -384,9 +413,39 @@ main (int argc, char* argv[])
fv4.epoch (),
fv4.canonical_upstream ()}));
assert (mrv[1].load () == fpv4);
- assert (fpv[3].load () == fpv4);
+ assert (fpv[4].load () == fpv4);
assert (check_location (fpv4));
+ // Verify libfoo package versions.
+ //
+ assert (fpv4->repository.load () == mr);
+ assert (fpv4->package.load () == pf);
+ assert (fpv4->version == version ("1.2.4-1"));
+ assert (fpv4->priority == priority::high);
+ assert (fpv4->changes.empty ());
+
+ assert (fpv4->license_alternatives.size () == 2);
+ assert (fpv4->license_alternatives[0].comment ==
+ "If using with GNU TLS.");
+ assert (fpv4->license_alternatives[0].size () == 2);
+ assert (fpv4->license_alternatives[0][0] == "LGPLv2");
+ assert (fpv4->license_alternatives[0][1] == "MIT");
+ assert (fpv4->license_alternatives[1].comment ==
+ "If using with OpenSSL.");
+ assert (fpv4->license_alternatives[1].size () == 1);
+ assert (fpv4->license_alternatives[1][0] == "BSD");
+
+ assert (fpv4->dependencies.size () == 1);
+ assert (fpv4->dependencies[0].size () == 1);
+
+ assert (fpv4->dependencies[0][0] ==
+ (dependency {
+ "libmisc",
+ brep::optional<version_comparison> (
+ version_comparison{version ("2.3.0"), comparison::ge})}));
+
+ assert (fpv4->requirements.empty ());
+
// Verify libexp package.
//
shared_ptr<package> pe (db.load<package> ("libexp"));
@@ -488,8 +547,8 @@ main (int argc, char* argv[])
// Update package summary, update package persistent state, rerun loader
// and ensure the model were not rebuilt.
//
- pf->summary = "test";
- db.update (pf);
+ pb->summary = "test";
+ db.update (pb);
t.commit ();
}
@@ -497,7 +556,7 @@ main (int argc, char* argv[])
assert (process (ld_args).wait ());
transaction t (db.begin ());
- assert (db.load<package> ("libfoo")->summary == "test");
+ assert (db.load<package> ("libbar")->summary == "test");
t.commit ();
}
// Fully qualified to avoid ambiguity with odb exception.
diff --git a/tests/loader/internal/1/math/packages b/tests/loader/internal/1/math/packages
index f2d7024..cbd8165 100644
--- a/tests/loader/internal/1/math/packages
+++ b/tests/loader/internal/1/math/packages
@@ -12,10 +12,16 @@ location: libexp-1+1.2.tar.gz
name: libfoo
version: 1.2.4-1
summary: The Foo Math Library
-description: Very good math library.
-license: MIT
+description: A modern C++ library with easy to use linear algebra and \
+optimization tools. There are over 100 functions in total with an extensive \
+test suite. The API is similar to MATLAB.
+license: LGPLv2, MIT; If using with GNU TLS.
+license: BSD; If using with OpenSSL.
+priority: high
tags: c++, foo, math
url: http://www.example.com/foo/
email: foo-users@example.com
+package-url: http://www.example.com/foo/pack
+package-email: pack@example.com
depends: libmisc >= 2.3.0
location: libfoo-1.2.4-1.tar.gz
diff --git a/tests/loader/internal/1/stable/packages b/tests/loader/internal/1/stable/packages
index b95aa62..d5f1551 100644
--- a/tests/loader/internal/1/stable/packages
+++ b/tests/loader/internal/1/stable/packages
@@ -51,3 +51,11 @@ depends: libmisc >= 2.0.0
changes: some changes 1
changes: some changes 2
location: libfoo-1.2.4.tar.gz
+:
+name: libfoo
+version: 1.0
+summary: The Foo Library
+license: MIT
+url: http://www.example.com/foo/
+email: foo-users@example.com
+location: libfoo-1.0.tar.gz
diff --git a/web/xhtml b/web/xhtml
index 708655c..d683469 100644
--- a/web/xhtml
+++ b/web/xhtml
@@ -278,6 +278,10 @@ namespace web
s.start_element (xmlns, "meta");
s.attribute ("charset", "UTF-8");
s.end_element ();
+ s.start_element (xmlns, "meta");
+ s.attribute ("name", "viewport");
+ s.attribute ("content", "device-width, initial-scale=1");
+ s.end_element ();
}
};
static const head_element HEAD;
@@ -297,24 +301,33 @@ namespace web
static const element BODY ("body");
static const element DIV ("div");
+ static const element H1 ("h1");
+ static const element H2 ("h2");
+ static const element H3 ("h3");
+ static const element H4 ("h4");
+ static const element H5 ("h5");
+ static const element H6 ("h6");
+ static const element META ("meta");
static const element P ("p");
static const element TITLE ("title");
- static const inline_element A ("a");
- static const inline_element B ("b");
- static const inline_element I ("i");
- static const inline_element U ("u");
-
- static const inline_element EM ("em");
- static const inline_element BR ("br");
+ static const inline_element A ("a");
+ static const inline_element B ("b");
+ static const inline_element BR ("br");
+ static const inline_element EM ("em");
+ static const inline_element I ("i");
+ static const inline_element SPAN ("span");
+ static const inline_element U ("u");
// Attributes.
//
- static const attribute CLASS ("class");
- static const attribute HREF ("href");
- static const attribute ID ("id");
- static const attribute STYLE ("style");
- static const attribute TYPE ("type");
+ static const attribute CLASS ("class");
+ static const attribute CONTENT ("content");
+ static const attribute HREF ("href");
+ static const attribute ID ("id");
+ static const attribute NAME ("name");
+ static const attribute STYLE ("style");
+ static const attribute TYPE ("type");
}
}