// file : mod/mod-repository-details.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include <mod/mod-repository-details.hxx> #include <algorithm> // max() #include <libstudxml/serializer.hxx> #include <odb/database.hxx> #include <odb/transaction.hxx> #include <libbutl/timestamp.mxx> // to_string() #include <web/xhtml.hxx> #include <web/module.hxx> #include <web/mime-url-encoding.hxx> #include <libbrep/package.hxx> #include <libbrep/package-odb.hxx> #include <mod/page.hxx> #include <mod/options.hxx> using namespace std; using namespace odb::core; using namespace brep::cli; // While currently the user-defined copy constructor is not required (we don't // need to deep copy nullptr's), it is a good idea to keep the placeholder // ready for less trivial cases. // brep::repository_details:: repository_details (const repository_details& r) : database_module (r), options_ (r.initialized_ ? r.options_ : nullptr) { } void brep::repository_details:: init (scanner& s) { HANDLER_DIAG; options_ = make_shared<options::repository_details> ( s, unknown_mode::fail, unknown_mode::fail); database_module::init (*options_, options_->package_db_retry ()); if (options_->root ().empty ()) options_->root (dir_path ("/")); } bool brep::repository_details:: handle (request& rq, response& rs) { using namespace web::xhtml; HANDLER_DIAG; const dir_path& root (options_->root ()); // Make sure no parameters passed. // try { name_value_scanner s (rq.parameters (1024)); params::repository_details (s, unknown_mode::fail, unknown_mode::fail); } catch (const cli::exception& e) { throw invalid_request (400, e.what ()); } static const string title ("About"); xml::serializer s (rs.content (), title); s << HTML << HEAD << TITLE << title << ~TITLE << CSS_LINKS (path ("repository-details.css"), root) << ~HEAD << BODY << DIV_HEADER (options_->logo (), options_->menu (), root, tenant) << DIV(ID="content"); transaction t (package_db_->begin ()); using query = query<repository>; for (const auto& r: package_db_->query<repository> ( (query::internal && query::id.tenant == tenant) + "ORDER BY" + query::priority)) { //@@ Feels like a lot of trouble (e.g., id_attribute()) for very // dubious value. A link to the package search page just for // this repository would probably be more useful. // string id (html_id (r.canonical_name)); s << H1(ID=id) << A(HREF="#" + web::mime_url_encode (id, false)) << r.display_name << ~A << ~H1; if (r.summary) s << H2 << *r.summary << ~H2; // Cleanup the URL fragment, if present. // repository_url u (r.location.url ()); u.fragment = nullopt; s << P << A(HREF=u.string ()) << r.location << ~A << *BR; if (r.email) { const email& e (*r.email); s << A(HREF="mailto:" + e) << e << ~A; if (!e.comment.empty ()) s << " (" << e.comment << ")"; s << *BR; } s << butl::to_string (max (r.packages_timestamp, r.repositories_timestamp), "%Y-%m-%d %H:%M:%S%[.N] %Z", true, true) << ~P; if (r.description) s << P_TEXT (*r.description); if (r.certificate) { const certificate& cert (*r.certificate); size_t np (cert.name.find (':')); assert (np != string::npos); // Naming scheme should always be present. // Mimic the suggested format of the repository description so that the // certificate info looks like just another section. Inside use the // format similar to the bpkg rep-info output. // s << P << "REPOSITORY CERTIFICATE" << ~P << P << "CN=" << cert.name.c_str () + np + 1 << *BR << "O=" << cert.organization << *BR << email (cert.email) << ~P << P(CLASS="certfp") << cert.fingerprint << ~P << PRE(CLASS="certpem") << cert.pem << ~PRE; } } t.commit (); s << ~DIV << ~BODY << ~HTML; return true; }