From bc26633fc731edf1aa9bd11c38e29f6c67eacc65 Mon Sep 17 00:00:00 2001 From: Yorhel Date: Sat, 27 Apr 2024 12:56:08 +0200 Subject: [PATCH] UI: Replace "locations" tab with separate page Removing the JS requirement and (hopefully) providing a more useful view into the same data. This view now also lists other man pages that happen to have the same contents. --- www/index.pl | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++- www/man.css | 4 +++ www/man.js | 9 ++---- 3 files changed, 91 insertions(+), 8 deletions(-) diff --git a/www/index.pl b/www/index.pl index 00cad42..ebc842b 100755 --- a/www/index.pl +++ b/www/index.pl @@ -4,6 +4,7 @@ use v5.26; use warnings; use TUWF ':html5_', ':xml'; use POSIX 'ceil'; +use List::Util 'uniq', 'min'; use SQL::Interp 'sql', 'sql_interp'; use Time::Local 'timegm'; @@ -842,7 +843,6 @@ sub man_page { framework_ title => $man->{name}, mainclass => 'manpage', sub { man_nav_ $man, $url, \@toc, \@htmllang; - # TODO: Replace the 'versions' and 'locations' functionality with non-JS alternatives. div_ id => 'manbuttons', sub { h1_ $man->{name}; ul_ 'data-hash' => $content->{hash}, @@ -853,6 +853,7 @@ sub man_page { sub { li_ sub { a_ href => $url->set(fmt => 'raw'), 'source' }; li_ sub { a_ href => $url->set(system => sysbyid->{$man->{system}}{short}, category => undef, shorthash => shorthash_to_hex $man->{shorthash}), 'permalink' }; + li_ sub { a_ href => "/loc/$content->{hash}", 'locations' }; } }; div_ id => 'manres', class => 'hidden', ''; @@ -1081,6 +1082,89 @@ TUWF::get qr{/sysredir/([^/]+)} => sub { tuwf->resRedirect('/man/'.(tuwf->reqGet TUWF::get qr{/lang/([^/]+)/([^/]+)} => sub { tuwf->resRedirect('/man.'.tuwf->capture(1).'/'.tuwf->capture(2), 'temp') }; +TUWF::get qr{/loc/([a-fA-F0-9]{40})}, sub { + my $hash = tuwf->capture(1); + + # There are a few files that have been duplicated far too many times for + # this page to be very useful. Add some limits to make sure the page at + # least manages to load something. + my $maxlisting = 30_000; + my $maxpersys = 500; + + my $l = tuwf->dbAlli(' + SELECT p.system, p.category, p.name AS package, v.version, f.filename, f.shorthash, m.name, m.section + FROM files f + JOIN mans m ON m.id = f.man + JOIN package_versions v ON v.id = f.pkgver + JOIN packages p ON p.id = v.package + WHERE f.content = (SELECT id FROM contents WHERE hash = decode(', \$hash, ", 'hex')) + ORDER BY p.system DESC, p.name, v.released DESC, f.filename + LIMIT ", \$maxlisting + ); + return tuwf->resNotFound() if !@$l; + + my %sys; + push $sys{ sysbyid->{$_->{system}}{name} }->@*, $_ for @$l; + + my @mans = uniq sort map "$_->{name}($_->{section})", @$l; + + framework_ title => 'Locations for '.join(', ', @mans), mainclass => 'locpage', sub { + h1_ 'Locations of this man page'; + p_ sub { + txt_ 'Listing all man pages identified by SHA1 '; + code_ $hash; + txt_ '.'; + if (@$l >= $maxlisting) { + br_; + b_ sprintf 'WARNING: This file has more than %d locations, the list below is incomplete.', $maxlisting; + } + br_; + br_; + txt_ 'Included man pages: '; + for (0..$#mans) { + txt_ ', ' if $_ > 0; + a_ href => "/man.".lc(substr($hash,0,8))."/".($mans[$_] =~ s/\(/./r =~ s/\)//r), $mans[$_]; + } + txt_ '.'; + if (keys %sys > 1) { + br_; + br_; + txt_ 'System index: '; + my @sys = sort keys %sys; + for (0..$#sys) { + txt_ ', ' if $_ > 0; + a_ href => "#$sys[$_]", $sys[$_]; + } + txt_ '.'; + } + }; + + for my $sysname (sort keys %sys) { + h2_ sub { a_ href => "#$sysname", id => "$sysname", $sysname }; + table_ sub { + thead_ sub { tr_ sub { + td_ 'Release' if sysbyid->{$sys{$sysname}[0]{system}}{release}; + td_ 'Package'; + td_ 'Path'; + }}; + tr_ sub { + my $sys = sysbyid->{$_->{system}}; + td_ sub { + txt_ $sys->{release}; + } if $sys->{release}; + td_ sub { + a_ href => "/pkg/$sys->{short}/$_->{category}/$_->{package}/$_->{version}", $_->{package}.'-'.$_->{version}; + small_ ' '.$_->{category}; + }; + td_ $_->{filename}; + } for @{$sys{$sysname}}[0..min $maxpersys, $#{$sys{$sysname}}]; + }; + small_ sprintf 'List truncated to the first %d results out of %d.', $maxpersys, scalar $sys{$sysname}->@* if $sys{$sysname}->@* > 500; + } + }; +}; + + TUWF::get '/json/tree.json' => sub { my $f = tuwf->validate(get => name => { default => '', maxlength => 256 }, diff --git a/www/man.css b/www/man.css index a1dbe15..66819ee 100644 --- a/www/man.css +++ b/www/man.css @@ -15,6 +15,7 @@ input { font-size: 0.9em; padding: 1px 2px } code { font-family: "Lucida Console", Monospace; font-size: 12px; background-color: #f0f8ff; padding: 1px } small { color: #aaa } .hidden { display: none!important; } +thead td { font-weight: bold } header { border-bottom: 3px dotted #ccc; display: flex; justify-content: space-between; align-items: end; flex-wrap: wrap } header a { font: 24px "Arial", serif; font-weight: bold } @@ -89,6 +90,9 @@ main.thin { max-width: 700px; margin: 0 auto } .manpage nav select { width: 200px } .manpage nav input { width: 40px } +.locpage tr td { padding: 2px 7px } +.locpage tbody tr td:last-child { font-size: 12px } + pre { margin: 10px 0 0 0 } pre, pre * { font-family: "Lucida Console", Monospace; font-size: 15px } diff --git a/www/man.js b/www/man.js index 6e22b4b..3a4af2c 100644 --- a/www/man.js +++ b/www/man.js @@ -445,17 +445,12 @@ function dsResults(hr, obj) { var section = ul.getAttribute('data-section'); var locale = ul.getAttribute('data-locale'); - ul.appendChild(tag('li', ul.getAttribute('data-hasversions') > 0 + ul.insertBefore(tag('li', ul.getAttribute('data-hasversions') > 0 ? tag('a', {href:'#', onclick: buttonclick, 'data-url': '/json/tree.json?name='+name+';section='+section+';locale='+locale+';cur='+hash, 'data-p': 'Different versions of this manual page are available.'}, 'versions') : tag('i', 'versions') - )); - - ul.appendChild(tag('li', tag('a', {href:'#', onclick: buttonclick, - 'data-url': '/json/tree.json?hash='+hash, - 'data-p': 'This manual page was found in the following locations.'}, - 'locations'))); + ), ul.childNodes[ul.childNodes.length-1]); })(); })();