mirror of
https://gitlab.com/depesz/explain.depesz.com.git
synced 2025-02-15 14:03:19 +02:00
Users almost done
Functionality is there, I'm just waiting for Metys to help me with styling
This commit is contained in:
parent
9ba0cc07cc
commit
b381985e43
@ -4,11 +4,13 @@
|
|||||||
"secret" : "|Erp--Wjgb)+eiB/|H=|V7!#+M|L{a8=J2|pd+N1=M|&pJWq|M&,f3q^XS",
|
"secret" : "|Erp--Wjgb)+eiB/|H=|V7!#+M|L{a8=J2|pd+N1=M|&pJWq|M&,f3q^XS",
|
||||||
|
|
||||||
"database" : {
|
"database" : {
|
||||||
"dsn" : "dbi:Pg:database=depesz_explain;host=127.0.0.1;port=5900",
|
"dsn" : "dbi:Pg:database=depesz_explain;host=127.0.0.1;port=5930",
|
||||||
"username" : "depesz_explain",
|
"username" : "depesz_explain",
|
||||||
"options" : {
|
"options" : {
|
||||||
"auto_commit" : 1,
|
"auto_commit" : 1,
|
||||||
"pg_server_prepare" : 0
|
"pg_server_prepare" : 0,
|
||||||
|
"RaiseError" : 1,
|
||||||
|
"PrintError" : 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -32,6 +32,12 @@ sub startup {
|
|||||||
# route: 'index'
|
# route: 'index'
|
||||||
$routes->route( '/' )->to( 'controller#index' )->name( 'new-explain' );
|
$routes->route( '/' )->to( 'controller#index' )->name( 'new-explain' );
|
||||||
|
|
||||||
|
# route: 'user-history'
|
||||||
|
$routes->route( '/user-history/:direction/:key' )->to( 'controller#user_history', direction => undef, key => undef )->name( 'user-history' );
|
||||||
|
|
||||||
|
# route: 'plan-change'
|
||||||
|
$routes->route( '/plan-change/:id' )->to( 'controller#plan_change' )->name( 'plan-change' );
|
||||||
|
|
||||||
# route: 'login'
|
# route: 'login'
|
||||||
$routes->route( '/login' )->to( 'controller#login' )->name( 'login' );
|
$routes->route( '/login' )->to( 'controller#login' )->name( 'login' );
|
||||||
|
|
||||||
|
@ -14,6 +14,24 @@ sub logout {
|
|||||||
$self->redirect_to( 'new-explain' );
|
$self->redirect_to( 'new-explain' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub user_history {
|
||||||
|
my $self = shift;
|
||||||
|
$self->redirect_to( 'history' ) unless $self->session->{'user'};
|
||||||
|
|
||||||
|
my @args = ( $self->session->{'user'} );
|
||||||
|
if (
|
||||||
|
( $self->param('direction') ) &&
|
||||||
|
( $self->param('direction') =~ m{\A(?:before|after)\z} ) &&
|
||||||
|
( $self->param('key') )
|
||||||
|
) {
|
||||||
|
push @args, $self->param('direction') eq 'before' ? 'DESC' : 'ASC';
|
||||||
|
push @args, $self->param('key');
|
||||||
|
}
|
||||||
|
my $data = $self->database->get_user_history( @args );
|
||||||
|
$self->stash->{'plans'} = $data;
|
||||||
|
return $self->render();
|
||||||
|
}
|
||||||
|
|
||||||
sub user {
|
sub user {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
@ -39,6 +57,64 @@ sub user {
|
|||||||
$self->stash->{'message'} = 'Changing the password failed.';
|
$self->stash->{'message'} = 'Changing the password failed.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub plan_change {
|
||||||
|
my $self = shift;
|
||||||
|
unless ( $self->session->{'user'} ) {
|
||||||
|
$self->app->log->error( 'User tried to access plan change without being logged!' );
|
||||||
|
$self->redirect_to( 'new-explain' );
|
||||||
|
}
|
||||||
|
$self->redirect_to( 'new-explain' ) unless $self->req->param('return');
|
||||||
|
|
||||||
|
my $plan = $self->database->get_plan_data( $self->param('id') );
|
||||||
|
if (
|
||||||
|
( ! defined $plan->{'added_by'} ) ||
|
||||||
|
( $plan->{'added_by'} ne $self->session->{'user'} )
|
||||||
|
) {
|
||||||
|
$self->app->log->error( 'User tried to access plan change for plan [' . $plan->{'id'} . '] of another user: ' . $self->session->{'user'});
|
||||||
|
$self->redirect_to( 'logout' );
|
||||||
|
}
|
||||||
|
|
||||||
|
# All looks fine. Current plan data are in $plan.
|
||||||
|
if (
|
||||||
|
( $self->req->param('delete') ) &&
|
||||||
|
( $self->req->param('delete') eq 'yes' )
|
||||||
|
) {
|
||||||
|
$self->database->delete_plan( $plan->{'id'}, $plan->{'delete_key'} );
|
||||||
|
return $self->redirect_to( $self->req->param('return') );
|
||||||
|
}
|
||||||
|
|
||||||
|
my %changes = ();
|
||||||
|
if ( $plan->{'title'} ne ( $self->req->param('title') // '' ) ) {
|
||||||
|
$changes{'title'} = ( $self->req->param('title') // '' );
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
( $plan->{'is_public'} ) &&
|
||||||
|
( ! $self->req->param('is_public') )
|
||||||
|
) {
|
||||||
|
$changes{ 'is_public' } = 0;
|
||||||
|
} elsif (
|
||||||
|
( $plan->{'is_public'} ) &&
|
||||||
|
( ! $self->req->param('is_public') )
|
||||||
|
) {
|
||||||
|
$changes{ 'is_public' } = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
( ! $plan->{'is_anonymized'}) &&
|
||||||
|
( $self->req->param('is_anonymized') )
|
||||||
|
) {
|
||||||
|
my $explain = Pg::Explain->new( source => $plan->{'plan'} );
|
||||||
|
$explain->anonymize();
|
||||||
|
$changes{'plan'} = $explain->as_text();
|
||||||
|
$changes{ 'is_anonymized' } = 1;
|
||||||
|
}
|
||||||
|
return $self->redirect_to( $self->req->param('return') ) if 0 == scalar keys %changes;
|
||||||
|
|
||||||
|
$self->database->update_plan( $plan->{'id'}, \%changes );
|
||||||
|
|
||||||
|
return $self->redirect_to( $self->req->param('return') );
|
||||||
|
}
|
||||||
|
|
||||||
sub login {
|
sub login {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
|
@ -109,6 +109,68 @@ sub user_change_password {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub get_user_history {
|
||||||
|
my $self = shift;
|
||||||
|
my ( $user, $direction, $marker ) = @_;
|
||||||
|
|
||||||
|
my $limit = 100;
|
||||||
|
|
||||||
|
$direction = 'DESC' if ( $direction // '' ) ne 'ASC';
|
||||||
|
my $query = '';
|
||||||
|
my @args = ();
|
||||||
|
|
||||||
|
if ( defined $marker ) {
|
||||||
|
my $comparison = $direction eq 'DESC' ? '<' : '>';
|
||||||
|
$query = "
|
||||||
|
SELECT p.id, p.entered_on::date, p.is_public, p.is_anonymized, p.title
|
||||||
|
FROM plans p
|
||||||
|
WHERE p.added_by = ? and not p.is_deleted
|
||||||
|
AND ( p.entered_on, p.id ) $comparison (
|
||||||
|
select x.entered_on, x.id
|
||||||
|
from plans x
|
||||||
|
where x.id = ?
|
||||||
|
)
|
||||||
|
ORDER BY p.entered_on $direction, p.id $direction LIMIT $limit
|
||||||
|
";
|
||||||
|
@args = ( $user, $marker );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$query = "
|
||||||
|
SELECT p.id, p.entered_on::date, p.is_public, p.is_anonymized, p.title
|
||||||
|
FROM plans p
|
||||||
|
WHERE p.added_by = ? and not p.is_deleted
|
||||||
|
ORDER BY p.entered_on DESC, p.id DESC LIMIT $limit
|
||||||
|
";
|
||||||
|
@args = ( $user );
|
||||||
|
}
|
||||||
|
my $plans = $self->dbh->selectall_arrayref( $query, { Slice => {} }, @args );
|
||||||
|
|
||||||
|
# newest plans always first
|
||||||
|
$plans = [ reverse @{ $plans } ] if $direction eq 'ASC';
|
||||||
|
|
||||||
|
return {
|
||||||
|
'list' => [],
|
||||||
|
'earlier' => 0,
|
||||||
|
'later' => 0,
|
||||||
|
} if 0 == scalar @{ $plans };
|
||||||
|
|
||||||
|
my @later = $self->dbh->selectrow_array(
|
||||||
|
'SELECT p.id FROM plans p where p.added_by = ? and not is_deleted and ( p.entered_on, p.id ) > ( select x.entered_on, x.id from plans x where x.id = ? ) limit 1',
|
||||||
|
undef,
|
||||||
|
$user, $plans->[0]->{'id'},
|
||||||
|
);
|
||||||
|
my @earlier = $self->dbh->selectrow_array(
|
||||||
|
'SELECT p.id FROM plans p where p.added_by = ? and not is_deleted and ( p.entered_on, p.id ) < ( select x.entered_on, x.id from plans x where x.id = ? ) limit 1',
|
||||||
|
undef,
|
||||||
|
$user, $plans->[-1]->{'id'},
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
'list' => $plans,
|
||||||
|
'later' => scalar @later,
|
||||||
|
'earlier' => scalar @earlier,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
sub get_pw_salt {
|
sub get_pw_salt {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my @salt_chars = ( 'a'..'z', 'A'..'Z', 0..9, '.', '/' );
|
my @salt_chars = ( 'a'..'z', 'A'..'Z', 0..9, '.', '/' );
|
||||||
@ -130,6 +192,24 @@ sub user_register {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub update_plan {
|
||||||
|
my $self = shift;
|
||||||
|
my ($id, $changes) = @_;
|
||||||
|
my @columns = keys %{ $changes };
|
||||||
|
my @values = values %{ $changes };
|
||||||
|
|
||||||
|
eval {
|
||||||
|
$self->dbh->do(
|
||||||
|
'UPDATE plans SET ' . join(', ', map { "$_ = ?" } @columns) . ' WHERE id = ?',
|
||||||
|
undef,
|
||||||
|
@values, $id
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return 1 unless $EVAL_ERROR;
|
||||||
|
$self->log->error( "update_plan( $id ) => " . $EVAL_ERROR );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
sub save_with_random_name {
|
sub save_with_random_name {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ( $title, $content, $is_public, $is_anon, $username ) = @_;
|
my ( $title, $content, $is_public, $is_anon, $username ) = @_;
|
||||||
@ -144,6 +224,20 @@ sub save_with_random_name {
|
|||||||
return @row;
|
return @row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub get_plan_data {
|
||||||
|
my $self = shift;
|
||||||
|
my ( $plan_id ) = @_;
|
||||||
|
|
||||||
|
my $rows = $self->dbh->selectall_arrayref(
|
||||||
|
'SELECT * FROM plans WHERE id = ? AND NOT is_deleted',
|
||||||
|
{ Slice => {} },
|
||||||
|
$plan_id,
|
||||||
|
);
|
||||||
|
return unless defined $rows;
|
||||||
|
return if 0 == scalar @{ $rows };
|
||||||
|
return $rows->[0];
|
||||||
|
}
|
||||||
|
|
||||||
sub get_plan {
|
sub get_plan {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ( $plan_id ) = @_;
|
my ( $plan_id ) = @_;
|
||||||
|
@ -6,6 +6,8 @@ CREATE TABLE users (
|
|||||||
|
|
||||||
ALTER TABLE plans add column added_by TEXT references users ( username );
|
ALTER TABLE plans add column added_by TEXT references users ( username );
|
||||||
|
|
||||||
|
create unique index concurrently plan_paging on plans (added_by, entered_on, id) where is_deleted = false;
|
||||||
|
|
||||||
DROP FUNCTION register_plan(in_title text, in_plan text, in_is_public boolean, in_is_anonymized boolean);
|
DROP FUNCTION register_plan(in_title text, in_plan text, in_is_public boolean, in_is_anonymized boolean);
|
||||||
CREATE FUNCTION register_plan(in_title text, in_plan text, in_is_public boolean, in_is_anonymized boolean, in_username text) RETURNS register_plan_return
|
CREATE FUNCTION register_plan(in_title text, in_plan text, in_is_public boolean, in_is_anonymized boolean, in_username text) RETURNS register_plan_return
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
|
64
templates/controller/user_history.html.ep
Normal file
64
templates/controller/user_history.html.ep
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
% layout 'default';
|
||||||
|
|
||||||
|
% my $title = 'User (' . session('user') . ') history';
|
||||||
|
% title $title;
|
||||||
|
|
||||||
|
<h1><%= $title %></h1>
|
||||||
|
|
||||||
|
% if ( scalar @{ $plans->{'list'} } ) {
|
||||||
|
% my $date = '';
|
||||||
|
<table border="1">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>id</th>
|
||||||
|
<th>title</th>
|
||||||
|
<th>public</th>
|
||||||
|
<th>anonymized</th>
|
||||||
|
<th>delete</th>
|
||||||
|
<th>action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
% for my $plan ( @{ $plans->{'list'}} ) {
|
||||||
|
|
||||||
|
% if ($plan->{'entered_on'} ne $date) {
|
||||||
|
<tr>
|
||||||
|
<th colspan="6"><%= $plan->{'entered_on'} %></th>
|
||||||
|
</tr>
|
||||||
|
% $date = $plan->{'entered_on'};
|
||||||
|
% }
|
||||||
|
|
||||||
|
<form id="plan-change" method="post" action="<%= url_for 'plan-change', 'id' => $plan->{'id'} %>">
|
||||||
|
<input type="hidden" name="return" value="<%= url_for 'current' %>"/>
|
||||||
|
<tr>
|
||||||
|
<td><a href="<%= url_for( 'show', id => $plan->{ id } ) %>"><%= $plan->{'id'} %></a></td>
|
||||||
|
<td><input type="text" name="title" value="<%= $plan->{'title'} %>"/></td>
|
||||||
|
<td><input type="checkbox" name="is_public" value="1" <% if ($plan->{'is_public'}) { %>checked="1"<% } %>></td>
|
||||||
|
<td>
|
||||||
|
% if ( $plan->{'is_anonymized'} ) {
|
||||||
|
YES
|
||||||
|
% } else {
|
||||||
|
<input type="checkbox" name="is_anonymized" value="1"/>
|
||||||
|
% }
|
||||||
|
</td>
|
||||||
|
<td><input type="text" name="delete" size="5"/></td>
|
||||||
|
<td><button name="submit" type="submit" value="change">Change!</button></td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
% }
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
% }
|
||||||
|
|
||||||
|
|
||||||
|
<div class="paginator">
|
||||||
|
% if ( $plans->{ later } ) {
|
||||||
|
<a class="newer" href="<%= url_for 'current', direction => 'after', 'key' => $plans->{'list'}->[0]->{'id'} %>" title="newer" rel="next">newer</a>
|
||||||
|
% }
|
||||||
|
% if ( $plans->{ earlier } ) {
|
||||||
|
<a class="older" href="<%= url_for 'current', direction => 'before', 'key' => $plans->{'list'}->[-1]->{'id'} %>" title="older" rel="prev">older</a>
|
||||||
|
% }
|
||||||
|
</div>
|
@ -78,22 +78,30 @@
|
|||||||
% }
|
% }
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="right">
|
<ul class="right">
|
||||||
<li>
|
|
||||||
% if ( session('user') ) {
|
% if ( session('user') ) {
|
||||||
|
<li>
|
||||||
% if ( $self->match->endpoint->name eq "user" ) {
|
% if ( $self->match->endpoint->name eq "user" ) {
|
||||||
<span>user: <%= session('user') %></span>
|
<span>user: <%= session('user') %></span>
|
||||||
% } else {
|
% } else {
|
||||||
<a href="<%= url_for 'user' %>" title="link to: user page" rel="permalink">user: <%= session('user') %></a>
|
<a href="<%= url_for 'user' %>" title="link to: user page" rel="permalink">user: <%= session('user') %></a>
|
||||||
% }
|
% }
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
% if ( $self->match->endpoint->name eq "user-history" ) {
|
||||||
|
<span>plans</span>
|
||||||
|
% } else {
|
||||||
|
<a href="<%= url_for 'user-history' %>" title="link to: user plans" rel="permalink">plans</a>
|
||||||
|
% }
|
||||||
|
</li>
|
||||||
% } else {
|
% } else {
|
||||||
|
<li>
|
||||||
% if ( $self->match->endpoint->name eq "login" ) {
|
% if ( $self->match->endpoint->name eq "login" ) {
|
||||||
<span>login</span>
|
<span>login</span>
|
||||||
% } else {
|
% } else {
|
||||||
<a href="<%= url_for 'login' %>" title="link to: login/register" rel="permalink">login</a>
|
<a href="<%= url_for 'login' %>" title="link to: login/register" rel="permalink">login</a>
|
||||||
% }
|
% }
|
||||||
|
</li>
|
||||||
% }
|
% }
|
||||||
<li>
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user