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.
This commit is contained in:
Yorhel 2024-04-27 12:56:08 +02:00
parent 9d650b00ec
commit bc26633fc7
3 changed files with 91 additions and 8 deletions

View file

@ -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 },

View file

@ -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 }

View file

@ -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]);
})();
})();