feat(dashboard): the UGC page replaces UGC Search

The UGC Search page and the UGC page searched the same things (owner,
account, property, the name and description a player gave a model, LOT,
UGC / blueprint id) with the same SQL. The UGC page's lists are now the
one search:

- models' List view has a Where column: placed on a property (with the
  name given there and a link to the model in the property's 3D view),
  in a mail or in the creator's inventories, or not found
  (/api/ugc?where=1, UgcLinks::Whereabouts as before)
- owners link to their character and, with accounts_view, account
- while searching, the kind buttons say how many models and how many
  cars and rockets match (/api/ugc "matches")
- /ugc_search?q= redirects to /ugc?view=list&q=; the menu's UGC Search
  entry is now UGC, which opens the UGC page (it was only under Server
  Admin, for ugc_manage)
- References lose their Find button (Where it is has the same) and name
  the inventory a build is in

The ugc-search page and script are removed; /api/ugc_links/search stays
for API users.

Check: the menu's UGC entry; an old /ugc_search?q= link opens the UGC
list with the search filled in; owner:, account:, property:, name:,
lot: and id: searches, and the counts on the kind buttons; the Where
column's links (property, 3D, character, account).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 06:14:47 -05:00
parent e159d7b768
commit fd141c232b
9 changed files with 91 additions and 140 deletions

View File

@@ -1,5 +1,6 @@
#include "UgcLinks.h"
#include <cctype>
#include <map>
#include <memory>
#include <set>
@@ -25,6 +26,17 @@ namespace {
constexpr size_t MAX_INVENTORIES = 25;
// A query value for an address: letters, digits and -._~ as they are, everything else %XX
std::string UrlEncode(std::string_view value) {
static constexpr char HEX[] = "0123456789ABCDEF";
std::string out;
for (const unsigned char c : value) {
if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') out += static_cast<char>(c);
else out += { '%', HEX[c >> 4], HEX[c & 15] };
}
return out;
}
// ---- Who may see a creation ----
// The property page's rule (as /api/properties/:id): properties_view, or the owner with own_properties
@@ -128,9 +140,14 @@ namespace UgcLinks {
}
void RegisterRoutes() {
Route(eHTTPMethod::GET, "/ugc_search", Perm("properties_view"), "Find players' models, cars and rockets and where they are",
// The UGC page's lists replaced the search page: its links (?q=) open them with the search filled in
Route(eHTTPMethod::GET, "/ugc_search", Perm("properties_view"), "Moved: the UGC page's lists (/ugc?view=list&q=)",
[](HTTPReply& reply, const HTTPContext& context) {
RenderPage(reply, context, "ugc-search.jinja2", "ugc_search", { { "query", QueryValue(context.queryString, "q").substr(0, 100) } });
const auto query = QueryValue(context.queryString, "q").substr(0, 100);
reply.status = eHTTPStatusCode::FOUND;
reply.location = "/ugc?view=list" + (query.empty() ? std::string() : "&q=" + UrlEncode(query));
reply.message = "";
reply.contentType = eContentType::TEXT_HTML;
});
Route(eHTTPMethod::GET, "/api/ugc_links/search", Perm("properties_view"),