aboutsummaryrefslogtreecommitdiff
path: root/brep/package-details.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'brep/package-details.cxx')
-rw-r--r--brep/package-details.cxx225
1 files changed, 225 insertions, 0 deletions
diff --git a/brep/package-details.cxx b/brep/package-details.cxx
new file mode 100644
index 0000000..f814ef8
--- /dev/null
+++ b/brep/package-details.cxx
@@ -0,0 +1,225 @@
+// file : brep/package-details.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <brep/package-details>
+
+#include <string>
+#include <memory> // make_shared(), shared_ptr
+#include <cstddef> // size_t
+
+#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 cli;
+using namespace odb::core;
+
+namespace brep
+{
+ void package_details::
+ init (scanner& s)
+ {
+ MODULE_DIAG;
+
+ options_ = make_shared<options::package_details> (
+ s, unknown_mode::fail, unknown_mode::fail);
+
+ db_ = shared_database (options_->db_host (), options_->db_port ());
+ }
+
+ template <typename T>
+ static inline query<T>
+ search_params (const string& n, const string& q)
+ {
+ using query = query<T>;
+
+ return "(" +
+ (q.empty ()
+ ? query ("NULL")
+ : "plainto_tsquery (" + query::_val (q) + ")") +
+ "," +
+ query::_val (n) +
+ ")";
+ }
+
+ void package_details::
+ handle (request& rq, response& rs)
+ {
+ using namespace xml;
+ using namespace web;
+ using namespace web::xhtml;
+
+ MODULE_DIAG;
+
+ const string& name (*rq.path ().rbegin ());
+ params::package_details pr;
+
+ try
+ {
+ param_scanner s (rq.parameters ());
+ pr = params::package_details (s, unknown_mode::fail, unknown_mode::fail);
+ }
+ catch (const unknown_argument& e)
+ {
+ throw invalid_request (400, e.what ());
+ }
+
+ const string& sq (pr.query ()); // Search query.
+ size_t pg (pr.page ());
+ bool f (pr.full ());
+ string en (mime_url_encode (name));
+ size_t rp (options_->results_on_page ());
+
+ auto url (
+ [&en](bool f = false,
+ const string& q = "",
+ size_t p = 0,
+ const string& a = "") -> string
+ {
+ string s ("?");
+ string u (en);
+
+ if (f) { u += "?full"; s = "&"; }
+ if (!q.empty ()) { u += s + "q=" + mime_url_encode (q); s = "&"; }
+ if (p > 0) { u += s + "p=" + to_string (p); s = "&"; }
+ if (!a.empty ()) { u += '#' + a; }
+ return u;
+ });
+
+ serializer s (rs.content (), name);
+ const string title (sq.empty () ? name : name + " " + sq);
+
+ s << HTML
+ << HEAD
+ << TITLE << title << ~TITLE
+ << CSS_LINKS ("/package-details.css")
+ << ~HEAD
+ << BODY
+ << DIV_HEADER ()
+ << DIV(ID="content");
+
+ if (f)
+ s << CLASS << "full" << ~CLASS;
+
+ s << DIV(ID="heading")
+ << H1 << A(HREF=url ()) << name << ~A << ~H1
+ << A(HREF=url (!f, sq, pg)) << (f ? "[brief]" : "[full]") << ~A
+ << ~DIV;
+
+ transaction t (db_->begin ());
+
+ shared_ptr<package> p;
+ {
+ latest_package lp;
+ if (!db_->query_one<latest_package> (
+ query<latest_package>(
+ "(" + query<latest_package>::_val (name) + ")"), lp))
+ {
+ throw invalid_request (404, "Package '" + name + "' not found");
+ }
+
+ p = db_->load<package> (lp.id);
+ }
+
+ const license_alternatives& ll (p->license_alternatives);
+
+ if (pg == 0)
+ {
+ // Display package details on the first page only.
+ //
+ s << H2 << p->summary << ~H2;
+
+ if (const auto& d = p->description)
+ s << (f
+ ? P_DESCRIPTION (*d)
+ : P_DESCRIPTION (
+ *d,
+ options_->description_length (),
+ url (!f, sq, pg, "description")));
+
+ s << TABLE(CLASS="proplist", ID="package")
+ << TBODY
+ << TR_LICENSE (ll)
+ << TR_URL (p->url)
+ << TR_EMAIL (p->email)
+ << TR_TAGS (p->tags)
+ << ~TBODY
+ << ~TABLE;
+ }
+
+ auto pc (
+ db_->query_value<package_count> (
+ search_params<package_count> (name, sq)));
+
+ auto r (
+ db_->query<package_search_rank> (
+ search_params<package_search_rank> (name, sq) +
+ "ORDER BY rank DESC, version_epoch DESC, "
+ "version_canonical_upstream DESC, version_revision DESC" +
+ "OFFSET" + to_string (pg * rp) +
+ "LIMIT" + to_string (rp)));
+
+ s << FORM_SEARCH (sq.c_str ())
+ << DIV_COUNTER (pc, "Version", "Versions")
+
+ // Enclose the subsequent tables to be able to use nth-child CSS selector.
+ //
+ << DIV;
+
+ for (const auto& pr: r)
+ {
+ shared_ptr<package> p (db_->load<package> (pr.id));
+
+ s << TABLE(CLASS="proplist version")
+ << TBODY
+ << TR_VERSION (name, p->version.string ())
+
+ // @@ Shouldn't we skip low priority row ?
+ //
+ << TR_PRIORITY (p->priority);
+
+ // Comparing objects of the license_alternatives class as being of the
+ // vector<vector<string>> class, so comments are not considered.
+ //
+ if (p->license_alternatives != ll)
+ s << TR_LICENSE (p->license_alternatives);
+
+ assert (p->internal_repository != nullptr);
+
+ // @@ Shouldn't we make package location to be a link to the proper
+ // place of the About page, describing corresponding repository ?
+ //
+ // @@ In most cases package location will be the same for all versions
+ // of the same package. Shouldn't we put package location to the
+ // package summary part and display it here only if it differes
+ // from the one in the summary ?
+ //
+ s << TR_LOCATION (p->internal_repository.object_id ())
+ << TR_DEPENDS (p->dependencies)
+ << TR_REQUIRES (p->requirements)
+ << ~TBODY
+ << ~TABLE;
+ }
+
+ t.commit ();
+
+ s << ~DIV
+ << DIV_PAGER (pg, pc, rp, options_->pages_in_pager (), url (f, sq))
+ << ~DIV
+ << ~BODY
+ << ~HTML;
+ }
+}