Add system selector to man pages + other stuff
The /man/* URLs now directly open man pages rather than redirecting to the permalink format, so that the extra source information can be used to provide a better UI for switching between systems and packages (currently only between systems). Step one in getting rid of the JS location/version selection thingies. Still quite a bit more to do, but I haven't worked it all out yet.
This commit is contained in:
parent
5194ed95cf
commit
c6f53fb0fb
2 changed files with 121 additions and 80 deletions
180
www/index.pl
180
www/index.pl
|
|
@ -398,9 +398,7 @@ TUWF::get '/info/about' => sub {
|
|||
Will get the man page from a specific package version (e.g. <a
|
||||
href="/man/ubuntu-xenial/net/rsync/3.1.1-3ubuntu1/rsync">/man/ubuntu-xenial/net/rsync/3.1.1-3ubuntu1/rsync</a>)</dd>
|
||||
</dl>
|
||||
<p>Currently, the last three URLs will perform a redirect to the
|
||||
appropriate permalink URL, but this may change in the future.<br />
|
||||
In all URLs where an optional <code>.<section></code> can be provided,
|
||||
<p>In all URLs where an optional <code>.<section></code> can be provided,
|
||||
the search is performed as a prefix match. For example, <a
|
||||
href="/cat.3">/cat.3</a> will provide the <code>cat.3tcl</code> man page if
|
||||
no exact <code>cat.3</code> version is available. Linking to the full
|
||||
|
|
@ -631,9 +629,18 @@ TUWF::get qr{/([^/]+)/([0-9a-f]{8})/src} => sub {
|
|||
};
|
||||
|
||||
|
||||
sub _man_nav {
|
||||
sub man_nav_ {
|
||||
my($man, $toc) = @_;
|
||||
|
||||
my @systems = tuwf->dbAlli('
|
||||
SELECT DISTINCT p.system
|
||||
FROM packages p
|
||||
JOIN package_versions v ON v.package = p.id
|
||||
JOIN files f ON f.pkgver = v.id
|
||||
JOIN mans m ON m.id = f.man
|
||||
WHERE m.name =', \$man->{name}, 'AND m.section =', \$man->{section}
|
||||
)->@*;
|
||||
|
||||
my @sect = map $_->{section}, tuwf->dbAlli(
|
||||
'SELECT DISTINCT section FROM mans WHERE name =', \$man->{name}, 'ORDER BY section'
|
||||
)->@*;
|
||||
|
|
@ -646,13 +653,32 @@ sub _man_nav {
|
|||
WHERE m.name =", \$man->{name}, 'AND m.section =', \$man->{section}, "
|
||||
ORDER BY substring(l.locale from '^[^.]+') NULLS FIRST"
|
||||
)->@*;
|
||||
return if !@sect && !@lang && !@$toc;
|
||||
|
||||
# TODO: This is ugly, especially because clicking on a translation or
|
||||
# section, you can end up with a man page that is nowhere close to the man
|
||||
# page you're currently reading. Opening a version selector box might be a
|
||||
# better alternative.
|
||||
div_ id => 'nav', sub {
|
||||
nav_ sub {
|
||||
form_ action => "/sysredir/$man->{name}.$man->{section}", method => 'get',
|
||||
onsubmit => 'location.href="/man/"+system_select[system_select.selectedIndex].value+"/'.$man->{name}.'.'.$man->{section}.'";return false',
|
||||
sub {
|
||||
my %names;
|
||||
push $names{$_->{name}}->@*, $_ for map sysbyid->{$_->{system}}, sort { $b->{system} <=> $a->{system} } @systems;
|
||||
select_ id => 'system_select', name => 'system', sub {
|
||||
for (sort { ($names{$b}->@* == 1) <=> ($names{$a}->@* == 1) || $a cmp $b } keys %names) {
|
||||
my $s = $names{$_};
|
||||
if (@$s == 1) {
|
||||
option_ value => $s->[0]{short}, selected => $s->[0]{id} == $man->{system}?'':undef, $s->[0]{full};
|
||||
next;
|
||||
}
|
||||
optgroup_ label => $_, sub {
|
||||
option_ value => $_->{short}, selected => $_->{id} == $man->{system}?'':undef, $_->{full} for @$s;
|
||||
};
|
||||
}
|
||||
};
|
||||
input_ type => 'submit', value => 'Go';
|
||||
} if @systems > 1;
|
||||
|
||||
# TODO: This is ugly, especially because clicking on a translation or
|
||||
# section, you can end up with a man page that is nowhere close to the
|
||||
# man page you're currently reading. Sections or languages available
|
||||
# for the currently selected system should be highlighted.
|
||||
if(@sect > 1) {
|
||||
b_ 'Sections';
|
||||
p_ sub {
|
||||
|
|
@ -719,22 +745,8 @@ sub soelim {
|
|||
}
|
||||
|
||||
|
||||
# This one has to go before the other mappings, to ensure that links work for
|
||||
# man pages called 'pkg' or 'man'. This also means that we can't have a
|
||||
# system named 8 hex digits, but at least that's easy to guarantee. :)
|
||||
TUWF::get qr{/(?<name>[^/]+)(?:/(?<hash>[0-9a-f]{8}))?} => sub {
|
||||
my $name = normalize_name tuwf->capture('name');
|
||||
my $shorthash = tuwf->capture('hash');
|
||||
|
||||
# Unfortunately, even in the permalink format with the hash, we don't know
|
||||
# from which package we're supposed to get the man page. This info is
|
||||
# needed in order to do .so substitution, so we can substitute files from
|
||||
# the same package as the requested man page. Use the man_pref logic here
|
||||
# to deterministically select a good package.
|
||||
my($man, undef) = $shorthash
|
||||
? man_pref undef, sql 'm.name =', \$name, 'AND f.shorthash =', shorthash_to_int($shorthash)
|
||||
: man_pref_name $name, 'true';
|
||||
return tuwf->resNotFound() if !$man->{name};
|
||||
sub man_page {
|
||||
my($man) = @_;
|
||||
|
||||
my $content = tuwf->dbRowi('SELECT encode(hash, \'hex\') AS hash, content FROM contents WHERE id =', \$man->{content});
|
||||
my $fmt = ManUtils::html ManUtils::fmt_block soelim $man->{verid}, $content->{content};
|
||||
|
|
@ -757,8 +769,9 @@ TUWF::get qr{/(?<name>[^/]+)(?:/(?<hash>[0-9a-f]{8}))?} => sub {
|
|||
);
|
||||
|
||||
tuwf->resLastMod($man->{released});
|
||||
framework_ title => $name, sub {
|
||||
_man_nav $man, \@toc;
|
||||
framework_ title => $man->{name}, mainclass => 'manpage', sub {
|
||||
man_nav_ $man, \@toc;
|
||||
# TODO: Replace the 'versions' and 'locations' functionality with non-JS alternatives.
|
||||
div_ id => 'manbuttons', sub {
|
||||
h1_ $man->{name};
|
||||
ul_ 'data-hash' => $content->{hash},
|
||||
|
|
@ -772,11 +785,61 @@ TUWF::get qr{/(?<name>[^/]+)(?:/(?<hash>[0-9a-f]{8}))?} => sub {
|
|||
}
|
||||
};
|
||||
div_ id => 'manres', class => 'hidden', '';
|
||||
|
||||
div_ id => 'contents', sub {
|
||||
pre_ sub { lit_ $fmt }
|
||||
}
|
||||
pre_ sub { lit_ $fmt };
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
# This one has to go before the other mappings, to ensure that links work for
|
||||
# man pages called 'pkg' or 'man'. This also means that we can't have a
|
||||
# system named 8 hex digits, but at least that's easy to guarantee. :)
|
||||
TUWF::get qr{/(?<name>[^/]+)(?:/(?<hash>[0-9a-f]{8}))?} => sub {
|
||||
my $name = normalize_name tuwf->capture('name');
|
||||
my $shorthash = tuwf->capture('hash');
|
||||
|
||||
# Unfortunately, even in the permalink format with the hash, we don't know
|
||||
# from which system and package we're supposed to get the man page. This
|
||||
# info is used in the UI and needed in order to do .so substitution, so we
|
||||
# can substitute files from the same package as the requested man page. Use
|
||||
# the man_pref logic here to deterministically select a good package.
|
||||
my($man, undef) = $shorthash
|
||||
? man_pref undef, sql 'm.name =', \$name, 'AND f.shorthash =', shorthash_to_int($shorthash)
|
||||
: man_pref_name $name, 'true';
|
||||
return tuwf->resNotFound() if !$man->{name};
|
||||
|
||||
man_page $man;
|
||||
};
|
||||
|
||||
|
||||
TUWF::get qr{/man/([^/]+)/(.+)} => sub {
|
||||
my($sys, $path) = tuwf->captures(1,2);
|
||||
|
||||
# Path can be:
|
||||
# 1. <name>
|
||||
# 2. <category>/<package>/<name>
|
||||
# 3. <category>/<package>/<version>/<name>
|
||||
|
||||
# $sys can be either a full system 'short' name, or a prefix (e.g. 'debian' meaning 'any debian-* version')
|
||||
my $sysid = sysbyshort->{$sys};
|
||||
$sysid = $sysid ? [$sysid->{id}] : [ map sysbyshort->{$_}{id}, grep /^\Q$sys\E-/, keys sysbyshort->%* ];
|
||||
return tuwf->resNotFound if !@$sysid;
|
||||
|
||||
my $shorthash = $path =~ s{/([a-fA-F0-9]{8})$}{} ? $1 : undef;
|
||||
my $man;
|
||||
if($path !~ m{/}) { # (1)
|
||||
($man) = man_pref_name $path, sql 's.id IN', $sysid;
|
||||
|
||||
} else {
|
||||
$path =~ s{/([^/]+)$}{};
|
||||
my $name = $1;
|
||||
|
||||
my($pkg, $ver) = pkg_frompath sql('system IN', $sysid), $path; # Handles (2) and (3)
|
||||
return tuwf->resNotFound if !$pkg;
|
||||
|
||||
($man) = man_pref_name $name, sql 's.id IN', $sysid, 'AND p.id =', \$pkg->{id}, $ver ? ('AND v.version =', \$ver) : ();
|
||||
}
|
||||
return tuwf->resNotFound if !$man;
|
||||
man_page $man;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -797,11 +860,10 @@ TUWF::get qr{/pkg/([^/]+)} => sub {
|
|||
'SELECT id, system, name, category, dead FROM', $packages_with_man, 'p WHERE', $where, 'ORDER BY name, category'
|
||||
);
|
||||
|
||||
my $title = $sys->{name}.($sys->{release}?" $sys->{release}":"");
|
||||
framework_ title => $title, mainclass => 'pkglist', sub {
|
||||
framework_ title => $sys->{full}, mainclass => 'pkglist', sub {
|
||||
div_ sub {
|
||||
div_ sub {
|
||||
h1_ $title;
|
||||
h1_ $sys->{full};
|
||||
};
|
||||
nav_ class => 'charselect', sub {
|
||||
for('all', 0, 'a'..'z') {
|
||||
|
|
@ -859,11 +921,11 @@ TUWF::get qr{/pkg/([^/]+)/(.+)} => sub {
|
|||
# Latest version of this package determines last modification date of the page.
|
||||
tuwf->resLastMod($vers->[0]{released});
|
||||
|
||||
my $sysname = $sys->{name}.($sys->{release}?" $sys->{release}":"");
|
||||
my $subtitle = " / $pkg->{category} / $pkg->{name}";
|
||||
framework_ title => "$sysname$subtitle $sel->{version}", mainclass => 'pkgpage', sub {
|
||||
my $pkgpath = "$sys->{short}/$pkg->{category}/$pkg->{name}";
|
||||
framework_ title => "$sys->{full}$subtitle $sel->{version}", mainclass => 'pkgpage', sub {
|
||||
h1_ sub {
|
||||
a_ href => "/pkg/$sys->{short}", $sysname;
|
||||
a_ href => "/pkg/$sys->{short}", $sys->{full};
|
||||
txt_ $subtitle;
|
||||
};
|
||||
|
||||
|
|
@ -872,7 +934,7 @@ TUWF::get qr{/pkg/([^/]+)/(.+)} => sub {
|
|||
h2_ 'Versions';
|
||||
ul_ sub {
|
||||
li_ sub {
|
||||
a_ href => "/pkg/$sys->{short}/$pkg->{category}/$pkg->{name}/$_->{version}", $_->{version} if $_ != $sel;
|
||||
a_ href => "/pkg/$pkgpath/$_->{version}", $_->{version} if $_ != $sel;
|
||||
b_ " $_->{version}" if $_ == $sel;
|
||||
small_ " $_->{released}";
|
||||
} for(@$vers);
|
||||
|
|
@ -881,15 +943,17 @@ TUWF::get qr{/pkg/([^/]+)/(.+)} => sub {
|
|||
|
||||
section_ sub {
|
||||
h2_ "Manuals for version $sel->{version}";
|
||||
paginate_ "/pkg/$sys->{short}/$pkg->{category}/$pkg->{name}/$sel->{version}?p=", $count, 200, $p;
|
||||
paginate_ "/pkg/$pkgpath/$sel->{version}?p=", $count, 200, $p;
|
||||
ul_ sub {
|
||||
li_ sub {
|
||||
a_ href => "/$_->{name}/".shorthash_to_hex($_->{shorthash}), "$_->{name}($_->{section})";
|
||||
# BUG: This URL should include the shorthash (or locale, at least),
|
||||
# because the same package may have multiple pages with the same name and section.
|
||||
a_ href => "/man/$pkgpath/$sel->{version}/$_->{name}.$_->{section}", "$_->{name}($_->{section})";
|
||||
b_ " $_->{locale}" if $_->{locale};
|
||||
small_ " $_->{filename}";
|
||||
} for(@$mans);
|
||||
};
|
||||
paginate_ "/pkg/$sys->{short}/$pkg->{category}/$pkg->{name}/$sel->{version}?p=", $count, 200, $p;
|
||||
paginate_ "/pkg/$pkgpath/$sel->{version}?p=", $count, 200, $p;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -906,37 +970,9 @@ TUWF::get qr{/browse/([^/]+)/([^/]+)(?:/([^/]+))?} => sub {
|
|||
tuwf->resRedirect("/pkg/$sys->{short}/$pkgs->{category}/$name".($ver ? "/$ver" :''), 'perm');
|
||||
};
|
||||
|
||||
# Redirects for canonical URLs
|
||||
TUWF::get qr{/man/([^/]+)/(.+)} => sub {
|
||||
my($sys, $path) = tuwf->captures(1,2);
|
||||
|
||||
# Path can be:
|
||||
# 1. <name>
|
||||
# 2. <category>/<package>/<name>
|
||||
# 3. <category>/<package>/<version>/<name>
|
||||
|
||||
# $sys can be either a full system 'short' name, or a prefix (e.g. 'debian' meaning 'any debian-* version')
|
||||
my $sysid = sysbyshort->{$sys};
|
||||
$sysid = $sysid ? [$sysid->{id}] : [ map sysbyshort->{$_}{id}, grep /^\Q$sys\E-/, keys sysbyshort->%* ];
|
||||
return tuwf->resNotFound if !@$sysid;
|
||||
|
||||
my $man;
|
||||
if($path !~ m{/}) { # (1)
|
||||
($man) = man_pref_name $path, sql 's.id IN', $sysid;
|
||||
|
||||
} else {
|
||||
$path =~ s{/([^/]+)$}{};
|
||||
my $name = $1;
|
||||
|
||||
my($pkg, $ver) = pkg_frompath sql('system IN', $sysid), $path; # Handles (2) and (3)
|
||||
return tuwf->resNotFound if !$pkg;
|
||||
|
||||
($man) = man_pref_name $name, sql 's.id IN', $sysid, 'AND p.id =', \$pkg->{id}, $ver ? ('AND v.version =', \$ver) : ();
|
||||
}
|
||||
return tuwf->resNotFound if !$man;
|
||||
|
||||
tuwf->resRedirect("/$man->{name}/".shorthash_to_hex($man->{shorthash}), 'temp');
|
||||
};
|
||||
# Redirect for the system selection box, for visitors who have disabled JS.
|
||||
TUWF::get qr{/sysredir/([^/]+)} => sub { tuwf->resRedirect('/man/'.(tuwf->reqGet('system')//'arch').'/'.tuwf->capture(1), 'temp') };
|
||||
|
||||
|
||||
# Redirect for a specific language for a man page.
|
||||
|
|
|
|||
21
www/man.css
21
www/man.css
|
|
@ -76,16 +76,21 @@ main.thin { max-width: 700px; margin: 0 auto }
|
|||
#manres table { margin: 2px 10px; }
|
||||
#manres table tr td:nth-child(1) { min-width: 80px }
|
||||
|
||||
#nav { background: #f0f8ff; color: #036; float: right; padding: 8px; width: 250px; margin-bottom: 10px; border-radius: 8px; }
|
||||
#nav b { text-transform: uppercase; font-size: 13px }
|
||||
#nav p { margin: 3px 5px 20px 5px }
|
||||
#nav p a, #nav p i { padding: 3px 5px; font-size: 13px; font-style: normal; text-decoration: none;}
|
||||
#nav p a:hover, #nav p i { background: #cde; border-radius: 5px }
|
||||
#nav ul { list-style-type: none; margin: 3px 10px 0 20px }
|
||||
#nav ul li a { overflow: hidden; margin-left: -10px; text-decoration: none; text-transform: capitalize }
|
||||
.manpage nav { background: #f0f8ff; color: #036; float: right; padding: 8px; width: 250px; margin-bottom: 10px; border-radius: 8px; }
|
||||
.manpage nav b { text-transform: uppercase; font-size: 13px }
|
||||
.manpage nav p { margin: 3px 5px 20px 5px }
|
||||
.manpage nav p a,
|
||||
.manpage nav p i { padding: 3px 5px; font-size: 13px; font-style: normal; text-decoration: none;}
|
||||
.manpage nav p a:hover,
|
||||
.manpage nav p i { background: #cde; border-radius: 5px }
|
||||
.manpage nav ul { list-style-type: none; margin: 3px 10px 0 20px }
|
||||
.manpage nav ul li a { overflow: hidden; margin-left: -10px; text-decoration: none; text-transform: capitalize }
|
||||
.manpage nav form { margin: 0 0 10px 0 }
|
||||
.manpage nav select { width: 200px }
|
||||
.manpage nav input { width: 40px }
|
||||
|
||||
#contents { margin: 10px 0 0 0 }
|
||||
|
||||
pre { margin: 10px 0 0 0 }
|
||||
pre, pre * { font-family: "Lucida Console", Monospace; font-size: 15px }
|
||||
pre b, pre em, pre a { color: #369; font-weight: normal; text-decoration: none }
|
||||
pre em { font-style: italic }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue