summaryrefslogtreecommitdiff
path: root/lib/VNDB/Handler
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2018-02-08 16:06:17 +0100
committerYorhel <git@yorhel.nl>2018-02-08 16:12:07 +0100
commit93b79ef9ebafcfccd0d239ffd06e2b547e209e3d (patch)
tree98d96f024d00bf318b16243518e53c2977136202 /lib/VNDB/Handler
parent3f3a4d9810bb2483a89442b85d438639f26ecb7e (diff)
Store d+ pages in the DB as versioned entries + use markdown
This touches a bunch of things: - Adds a new first-class database entry type - Removes the d+.+.+ BBCode link syntax, adds a new d+#+ and d+#+.+ link syntax (references have been updated where possible) - Adds a new dependency on Text::MultiMarkdown
Diffstat (limited to 'lib/VNDB/Handler')
-rw-r--r--lib/VNDB/Handler/Discussions.pm4
-rw-r--r--lib/VNDB/Handler/Docs.pm177
-rw-r--r--lib/VNDB/Handler/Misc.pm84
-rw-r--r--lib/VNDB/Handler/VNEdit.pm2
4 files changed, 190 insertions, 77 deletions
diff --git a/lib/VNDB/Handler/Discussions.pm b/lib/VNDB/Handler/Discussions.pm
index f7f26a5e..f6b68c36 100644
--- a/lib/VNDB/Handler/Discussions.pm
+++ b/lib/VNDB/Handler/Discussions.pm
@@ -304,7 +304,7 @@ sub edit {
!$tid || $num == 1 ? (
[ input => short => 'title', name => 'Thread title' ],
[ input => short => 'boards', name => 'Board(s)' ],
- [ static => content => 'Read <a href="/d9.2">d9.2</a> for information about how to specify boards.' ],
+ [ static => content => 'Read <a href="/d9#2">d9#2</a> for information about how to specify boards.' ],
$self->authCan('boardmod') ? (
[ check => name => 'Locked', short => 'locked' ],
) : (),
@@ -318,7 +318,7 @@ sub edit {
) : (),
) : (),
[ text => name => 'Message<br /><b class="standout">English please!</b>', short => 'msg', rows => 25, cols => 75 ],
- [ static => content => 'See <a href="/d9.3">d9.3</a> for the allowed formatting codes' ],
+ [ static => content => 'See <a href="/d9#3">d9#3</a> for the allowed formatting codes' ],
(!$tid || $num == 1) ? (
[ static => content => '<br />' ],
[ check => short => 'poll', name => 'Add poll' ],
diff --git a/lib/VNDB/Handler/Docs.pm b/lib/VNDB/Handler/Docs.pm
new file mode 100644
index 00000000..bc1166d5
--- /dev/null
+++ b/lib/VNDB/Handler/Docs.pm
@@ -0,0 +1,177 @@
+
+package VNDB::Handler::Docs;
+
+
+use strict;
+use warnings;
+use TUWF ':html';
+use VNDB::Func;
+use Text::MultiMarkdown 'markdown';
+
+
+TUWF::register(
+ qr{d([1-9]\d*)(?:\.([1-9]\d*))?} => \&page,
+ qr{d([1-9]\d*)(?:\.([1-9]\d*))?/edit} => \&edit,
+);
+
+
+sub _html {
+ my $content = shift;
+
+ $content =~ s{^:MODERATORS:$}{
+ my $l = tuwf->dbUserGet(results => 100, sort => 'id', notperm => tuwf->{default_perm}, what => 'extended');
+ my $admin = 0;
+ $admin |= $_ for values %{ tuwf->{permissions} };
+ '<dl>'.join('', map {
+ my $u = $_;
+ my $p = $u->{perm} >= $admin ? 'admin' : join ', ', sort map +($u->{perm} &~ tuwf->{default_perm}) & tuwf->{permissions}{$_} ? $_ : (), keys %{ tuwf->{permissions} };
+ $p ? sprintf('<dt><a href="/u%d">%s</a></dt><dd>%s</dd>', $_->{id}, $_->{username}, $p) : ()
+ } @$l).'</dl>';
+ }me;
+ $content =~ s{^:SKINCONTRIB:$}{
+ my %users;
+ push @{$users{ tuwf->{skins}{$_}[1] }}, [ $_, tuwf->{skins}{$_}[0] ]
+ for sort { tuwf->{skins}{$a}[0] cmp tuwf->{skins}{$b}[0] } keys %{ tuwf->{skins} };
+ my $u = tuwf->dbUserGet(uid => [ keys %users ]);
+ '<dl>'.join('', map sprintf('<dt><a href="/u%d">%s</a></dt><dd>%s</dd>',
+ $_->{id}, $_->{username}, join(', ', map sprintf('<a href="?skin=%s">%s</a>', $_->[0], $_->[1]), @{$users{$_->{id}}})
+ ), @$u).'</dl>';
+ }me;
+
+ my $html = markdown $content, {
+ strip_metadata => 1,
+ img_ids => 0,
+ disable_footnotes => 1,
+ disable_bibliography => 1,
+ };
+
+ # Number sections and turn them into links
+ my($sec, $subsec) = (0,0);
+ $html =~ s{<h([1-2])[^>]+>(.*?)</h\1>}{
+ if($1 == 1) {
+ $sec++;
+ $subsec = 0;
+ qq{<h3><a href="#$sec" name="$sec">$sec. $2</a></h3>}
+ } elsif($1 == 2) {
+ $subsec++;
+ qq|<h4><a href="#$sec.$subsec" name="$sec.$subsec">$sec.$subsec. $2</a></h4>\n|
+ }
+ }ge;
+
+ # Text::MultiMarkdown doesn't handle fenced code blocks properly. The
+ # following solution breaks inline code blocks, but I don't use those anyway.
+ $html =~ s/<code>/<pre>/g;
+ $html =~ s#</code>#</pre>#g;
+
+ $html
+}
+
+
+sub page {
+ my($self, $id, $rev) = @_;
+
+ my $method = $rev ? 'dbDocGetRev' : 'dbDocGet';
+ my $d = $self->$method(id => $id, $rev ? ( rev => $rev ) : ())->[0];
+ return $self->resNotFound if !$d->{id};
+
+ $self->htmlHeader(title => $d->{title}, noindex => $rev);
+ $self->htmlMainTabs(d => $d);
+ return if $self->htmlHiddenMessage('d', $d);
+
+ if($rev) {
+ my $prev = $rev && $rev > 1 && $self->dbDocGetRev(id => $id, rev => $rev-1)->[0];
+ $self->htmlRevision('d', $prev, $d,
+ [ title => 'Title', diff => 1 ],
+ [ content => 'Content', diff => qr/\s+/ ],
+ );
+ }
+
+ div class => 'mainbox';
+ h1 $d->{title};
+ div class => 'docs';
+ ul class => 'index';
+ li; b 'Guidelines'; end;
+ li; a href => '/d5', 'Editing Guidelines'; end;
+ li; a href => '/d2', 'Visual Novels'; end;
+ li; a href => '/d15', 'Special Games'; end;
+ li; a href => '/d3', 'Releases'; end;
+ li; a href => '/d4', 'Producers'; end;
+ li; a href => '/d16', 'Staff'; end;
+ li; a href => '/d12', 'Characters'; end;
+ li; a href => '/d10', 'Tags & Traits'; end;
+ li; a href => '/d13', 'Capturing Screenshots'; end;
+ li; b 'About VNDB'; end;
+ li; a href => '/d9', 'Discussion Board'; end;
+ li; a href => '/d6', 'FAQ'; end;
+ li; a href => '/d7', 'About Us'; end;
+ li; a href => '/d11', 'Database API'; end;
+ li; a href => '/d14', 'Database Dumps'; end;
+ end;
+ lit _html $d->{content};
+ end;
+ end;
+ $self->htmlFooter;
+}
+
+
+sub edit {
+ my($self, $id, $rev) = @_;
+
+ my $d = $self->dbDocGetRev(id => $id, rev => $rev)->[0];
+ return $self->resNotFound if !$d->{id};
+ $rev = undef if $d->{lastrev};
+
+ return $self->htmlDenied if !$self->authCan('dbmod');
+
+ my %b4 = map { $_ => $d->{$_} } qw|title content ihid ilock|;
+ my $frm;
+
+ if($self->reqMethod eq 'POST') {
+ return if !$self->authCheckCode;
+ $frm = $self->formValidate(
+ { post => 'title', maxlength => 200 },
+ { post => 'content', },
+ { post => 'editsum', template => 'editsum' },
+ { post => 'ihid', required => 0 },
+ { post => 'ilock', required => 0 },
+ { post => 'preview', required => 0 },
+ );
+ if(!$frm->{_err} && !$frm->{preview}) {
+ $frm->{ihid} = $frm->{ihid}?1:0;
+ $frm->{ilock} = $frm->{ilock}?1:0;
+
+ return $self->resRedirect("/d$id", 'post') if !form_compare(\%b4, $frm);
+ my $nrev = $self->dbItemEdit(d => $id, $d->{rev}, %$frm);
+ return $self->resRedirect("/d$nrev->{itemid}.$nrev->{rev}", 'post');
+ }
+ }
+
+ !defined $frm->{$_} && ($frm->{$_} = $b4{$_}) for keys %b4;
+ $frm->{editsum} = sprintf 'Reverted to revision d%d.%d', $id, $rev if $rev && !defined $frm->{editsum};
+ delete $frm->{_err} if $frm->{preview};
+
+ my $title = "Edit $d->{title}";
+ $self->htmlHeader(title => $title, noindex => 1);
+ $self->htmlMainTabs('d', $d, 'edit');
+
+ if($frm->{preview}) {
+ div class => 'mainbox';
+ h1 'Preview';
+ div class => 'docs';
+ lit _html $frm->{content};
+ end;
+ end;
+ }
+
+ $self->htmlForm({ frm => $frm, action => "/d$id/edit", editsum => 1, preview => 1 }, dedit => [ $title,
+ [ input => name => 'Title', short => 'title', width => 300 ],
+ [ static => nolabel => 1, content => q{
+ <br>Contents (HTML and MultiMarkdown supported, which is
+ <a href="https://daringfireball.net/projects/markdown/basics">Markdown</a>
+ with some <a href="http://fletcher.github.io/MultiMarkdown-5/syntax.html">extensions</a>).} ],
+ [ textarea => short => 'content', name => 'Content', rows => 50, cols => 90, nolabel => 1 ],
+ ]);
+ $self->htmlFooter;
+}
+
+1;
diff --git a/lib/VNDB/Handler/Misc.pm b/lib/VNDB/Handler/Misc.pm
index e3b67d52..771c9d83 100644
--- a/lib/VNDB/Handler/Misc.pm
+++ b/lib/VNDB/Handler/Misc.pm
@@ -4,18 +4,16 @@ package VNDB::Handler::Misc;
use strict;
use warnings;
-use TUWF ':html', ':xml', 'xml_escape', 'uri_escape';
+use TUWF ':html', ':xml', 'uri_escape';
use VNDB::Func;
-use POSIX 'strftime';
TUWF::register(
- qr{}, \&homepage,
- qr{(?:([upvrcs])([1-9]\d*)/)?hist},\&history,
- qr{d([1-9]\d*)}, \&docpage,
- qr{nospam}, \&nospam,
- qr{xml/prefs\.xml}, \&prefs,
- qr{opensearch\.xml}, \&opensearch,
+ qr{}, \&homepage,
+ qr{(?:([upvrcsd])([1-9]\d*)/)?hist},\&history,
+ qr{nospam}, \&nospam,
+ qr{xml/prefs\.xml}, \&prefs,
+ qr{opensearch\.xml}, \&opensearch,
# redirects for old URLs
qr{u([1-9]\d*)/tags}, sub { $_[0]->resRedirect("/g/links?u=$_[1]", 'perm') },
@@ -28,8 +26,6 @@ TUWF::register(
sub { $_[0]->resRedirect("/v$_[1]", 'perm') },
qr{u/list(/[a-z0]|/all)?},
sub { my $l = defined $_[1] ? $_[1] : '/all'; $_[0]->resRedirect("/u$l", 'perm') },
- qr{d([1-9]\d*)\.([1-9]\d*)},
- sub { $_[0]->resRedirect("/d$_[1]#$_[2]", 'perm') }
);
@@ -206,7 +202,7 @@ sub history {
{ get => 'p', required => 0, default => 1, template => 'page' },
{ get => 'm', required => 0, default => !$type, enum => [ 0, 1 ] },
{ get => 'h', required => 0, default => 0, enum => [ -1..1 ] },
- { get => 't', required => 0, default => '', enum => [qw|v r p c s a|] },
+ { get => 't', required => 0, default => '', enum => [qw|v r p c s d a|] },
{ get => 'e', required => 0, default => 0, enum => [ -1..1 ] },
{ get => 'r', required => 0, default => 0, enum => [ 0, 1 ] },
);
@@ -218,6 +214,7 @@ sub history {
$type eq 'r' ? $self->dbReleaseGet(id => $id)->[0] :
$type eq 'c' ? $self->dbCharGet(id => $id)->[0] :
$type eq 's' ? $self->dbStaffGet(id => $id)->[0] :
+ $type eq 'd' ? $self->dbDocGet(id => $id)->[0] :
$type eq 'v' ? $self->dbVNGet(id => $id)->[0] : undef;
return $self->resNotFound if $type && !$obj->{id};
my $title = $type ? 'Edit history of '.($obj->{title} || $obj->{name} || $obj->{username}) : 'Recent changes';
@@ -226,7 +223,7 @@ sub history {
my($list, $np) = $self->dbRevisionGet(
$type && $type ne 'u' ? ( type => $type, itemid => $id ) : (),
$type eq 'u' ? ( uid => $id ) : (),
- $f->{t} ? ( type => $f->{t} eq 'a' ? [qw|v r p s|] : $f->{t} ) : (),
+ $f->{t} ? ( type => $f->{t} eq 'a' ? [qw|v r p s d|] : $f->{t} ) : (),
page => $f->{p},
results => 50,
auto => $f->{m},
@@ -273,6 +270,7 @@ sub history {
a $f->{t} eq 'p' ? (class => 'optselected') : (), href => $u->(t => 'p'), 'Only producers';
a $f->{t} eq 's' ? (class => 'optselected') : (), href => $u->(t => 's'), 'Only staff';
a $f->{t} eq 'c' ? (class => 'optselected') : (), href => $u->(t => 'c'), 'Only characters';
+ a $f->{t} eq 'd' ? (class => 'optselected') : (), href => $u->(t => 'd'), 'Only docs';
a $f->{t} eq 'a' ? (class => 'optselected') : (), href => $u->(t => 'a'), 'All except characters';
end;
p class => 'browseopts';
@@ -294,68 +292,6 @@ sub history {
}
-sub docpage {
- my($self, $did) = @_;
-
- my $f = sprintf('%s/data/docs/%d', $VNDB::ROOT, $did);
- my $F;
- open($F, '<:utf8', $f) or return $self->resNotFound;
- my @c = <$F>;
- close $F;
-
- (my $title = shift @c) =~ s/^:TITLE://;
- chomp $title;
-
- my($sec, $subsec) = (0,0);
- for (@c) {
- s{^:SUB:(.+)\r?\n$}{
- $sec++;
- $subsec = 0;
- qq|<h3><a href="#$sec" name="$sec">$sec. $1</a></h3>\n|
- }e;
- s{^:SUBSUB:(.+)\r?\n$}{
- $subsec++;
- qq|<h4><a href="#$sec.$subsec" name="$sec.$subsec">$sec.$subsec. $1</a></h4>\n|
- }e;
- s{^:INC:(.+)\r?\n$}{
- $f = sprintf('%s/data/docs/%s', $VNDB::ROOT, $1);
- open($F, '<:utf8', $f) or die $!;
- my $ii = join('', <$F>);
- close $F;
- $ii;
- }e;
- s{^:MODERATORS:$}{
- my $l = $self->dbUserGet(results => 100, sort => 'id', notperm => $self->{default_perm}, what => 'extended');
- my $admin = 0;
- $admin |= $_ for values %{$self->{permissions}};
- '<dl>'.join('', map {
- my $u = $_;
- my $p = $u->{perm} >= $admin ? 'admin' : join ', ', sort map +($u->{perm} &~ $self->{default_perm}) & $self->{permissions}{$_} ? $_ : (), keys %{$self->{permissions}};
- $p ? sprintf('<dt><a href="/u%d">%s</a></dt><dd>%s</dd>', $_->{id}, $_->{username}, $p) : ()
- } @$l).'</dl>';
- }e;
- s{^:SKINCONTRIB:$}{
- my %users;
- push @{$users{ $self->{skins}{$_}[1] }}, [ $_, $self->{skins}{$_}[0] ]
- for sort { $self->{skins}{$a}[0] cmp $self->{skins}{$b}[0] } keys %{$self->{skins}};
- my $u = $self->dbUserGet(uid => [ keys %users ]);
- '<dl>'.join('', map sprintf('<dt><a href="/u%d">%s</a></dt><dd>%s</dd>',
- $_->{id}, $_->{username}, join(', ', map sprintf('<a href="?skin=%s">%s</a>', $_->[0], $_->[1]), @{$users{$_->{id}}})
- ), @$u).'</dl>';
- }e;
- }
-
- $self->htmlHeader(title => $title);
- div class => 'mainbox';
- h1 $title;
- div class => 'docs';
- lit join '', @c;
- end;
- end;
- $self->htmlFooter;
-}
-
-
sub nospam {
my $self = shift;
$self->htmlHeader(title => 'Could not send form', noindex => 1);
diff --git a/lib/VNDB/Handler/VNEdit.pm b/lib/VNDB/Handler/VNEdit.pm
index ce10611d..e08925f7 100644
--- a/lib/VNDB/Handler/VNEdit.pm
+++ b/lib/VNDB/Handler/VNEdit.pm
@@ -319,7 +319,7 @@ sub _form {
@alist ? @{$self->dbStaffGet(aid => \@alist, results => 200)} : ()
};
div class => 'warning';
- lit 'Please check the <a href="/d2.3">staff editing guidelines</a>. You can'
+ lit 'Please check the <a href="/d2#3">staff editing guidelines</a>. You can'
.' <a href="/s/new">create a new staff entry</a> if it is not in the database yet,'
.' but please <a href="/s/all">check for aliasses first</a>.';
end;