1
0
mirror of https://gitlab.com/depesz/explain.depesz.com.git synced 2024-11-24 08:42:27 +02:00

Users almost done

Functionality is there, I'm just waiting for Metys to help me with styling
This commit is contained in:
Hubert depesz Lubaczewski 2013-10-29 14:35:04 +01:00
parent 9ba0cc07cc
commit b381985e43
7 changed files with 257 additions and 5 deletions

View File

@ -4,11 +4,13 @@
"secret" : "|Erp--Wjgb)+eiB/|H=|V7!#+M|L{a8=J2|pd+N1=M|&pJWq|M&,f3q^XS",
"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",
"options" : {
"auto_commit" : 1,
"pg_server_prepare" : 0
"pg_server_prepare" : 0,
"RaiseError" : 1,
"PrintError" : 0
}
},

View File

@ -32,6 +32,12 @@ sub startup {
# route: 'index'
$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'
$routes->route( '/login' )->to( 'controller#login' )->name( 'login' );

View File

@ -14,6 +14,24 @@ sub logout {
$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 {
my $self = shift;
@ -39,6 +57,64 @@ sub user {
$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 {
my $self = shift;

View File

@ -109,6 +109,68 @@ sub user_change_password {
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 {
my $self = shift;
my @salt_chars = ( 'a'..'z', 'A'..'Z', 0..9, '.', '/' );
@ -130,6 +192,24 @@ sub user_register {
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 {
my $self = shift;
my ( $title, $content, $is_public, $is_anon, $username ) = @_;
@ -144,6 +224,20 @@ sub save_with_random_name {
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 {
my $self = shift;
my ( $plan_id ) = @_;

View File

@ -6,6 +6,8 @@ CREATE TABLE users (
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);
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

View 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>

View File

@ -78,22 +78,30 @@
% }
</ul>
<ul class="right">
<li>
% if ( session('user') ) {
<li>
% if ( $self->match->endpoint->name eq "user" ) {
<span>user: <%= session('user') %></span>
% } else {
<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 {
<li>
% if ( $self->match->endpoint->name eq "login" ) {
<span>login</span>
% } else {
<a href="<%= url_for 'login' %>" title="link to: login/register" rel="permalink">login</a>
% }
</li>
% }
<li>
</ul>
</nav>