add profile editor
This commit is contained in:
parent
d4a6470141
commit
0172f0ce8a
6 changed files with 204 additions and 9 deletions
|
@ -2213,6 +2213,7 @@ sub startup {
|
||||||
|
|
||||||
$authed_r->get('/account')->to('account#account');
|
$authed_r->get('/account')->to('account#account');
|
||||||
$authed_r->get('/account/privacy')->to('account#privacy');
|
$authed_r->get('/account/privacy')->to('account#privacy');
|
||||||
|
$authed_r->get('/account/profile')->to('account#profile');
|
||||||
$authed_r->get('/account/hooks')->to('account#webhook');
|
$authed_r->get('/account/hooks')->to('account#webhook');
|
||||||
$authed_r->get('/account/traewelling')->to('traewelling#settings');
|
$authed_r->get('/account/traewelling')->to('traewelling#settings');
|
||||||
$authed_r->get('/account/insight')->to('account#insight');
|
$authed_r->get('/account/insight')->to('account#insight');
|
||||||
|
@ -2239,6 +2240,7 @@ sub startup {
|
||||||
$authed_r->get('/s/*station')->to('traveling#station');
|
$authed_r->get('/s/*station')->to('traveling#station');
|
||||||
$authed_r->get('/confirm_mail/:token')->to('account#confirm_mail');
|
$authed_r->get('/confirm_mail/:token')->to('account#confirm_mail');
|
||||||
$authed_r->post('/account/privacy')->to('account#privacy');
|
$authed_r->post('/account/privacy')->to('account#privacy');
|
||||||
|
$authed_r->post('/account/profile')->to('account#profile');
|
||||||
$authed_r->post('/account/hooks')->to('account#webhook');
|
$authed_r->post('/account/hooks')->to('account#webhook');
|
||||||
$authed_r->post('/account/traewelling')->to('traewelling#settings');
|
$authed_r->post('/account/traewelling')->to('traewelling#settings');
|
||||||
$authed_r->post('/account/insight')->to('account#insight');
|
$authed_r->post('/account/insight')->to('account#insight');
|
||||||
|
|
|
@ -7,6 +7,8 @@ use Mojo::Base 'Mojolicious::Controller';
|
||||||
|
|
||||||
use Crypt::Eksblowfish::Bcrypt qw(bcrypt en_base64);
|
use Crypt::Eksblowfish::Bcrypt qw(bcrypt en_base64);
|
||||||
use JSON;
|
use JSON;
|
||||||
|
use Mojo::Util qw(xml_escape);
|
||||||
|
use Text::Markdown;
|
||||||
use UUID::Tiny qw(:std);
|
use UUID::Tiny qw(:std);
|
||||||
|
|
||||||
my %visibility_itoa = (
|
my %visibility_itoa = (
|
||||||
|
@ -499,6 +501,72 @@ sub privacy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub profile {
|
||||||
|
my ($self) = @_;
|
||||||
|
my $user = $self->current_user;
|
||||||
|
|
||||||
|
if ( $self->param('action') and $self->param('action') eq 'save' ) {
|
||||||
|
if ( $self->validation->csrf_protect->has_error('csrf_token') ) {
|
||||||
|
$self->render(
|
||||||
|
'edit_profile',
|
||||||
|
invalid => 'csrf',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
my $md = Text::Markdown->new;
|
||||||
|
my $bio = $self->param('bio');
|
||||||
|
|
||||||
|
if ( length($bio) > 2000 ) {
|
||||||
|
$bio = substr( $bio, 0, 2000 ) . '…';
|
||||||
|
}
|
||||||
|
|
||||||
|
my $profile = {
|
||||||
|
bio => {
|
||||||
|
markdown => $bio,
|
||||||
|
html => $md->markdown( xml_escape($bio) ),
|
||||||
|
},
|
||||||
|
metadata => [],
|
||||||
|
};
|
||||||
|
for my $i ( 0 .. 20 ) {
|
||||||
|
my $key = $self->param("key_$i");
|
||||||
|
my $value = $self->param("value_$i");
|
||||||
|
if ($key) {
|
||||||
|
if ( length($value) > 500 ) {
|
||||||
|
$value = substr( $value, 0, 500 ) . '…';
|
||||||
|
}
|
||||||
|
my $html_value
|
||||||
|
= ( $value
|
||||||
|
=~ s{ \[ ([^]]+) \]\( ([^)]+) \) }{'<a href="' . xml_escape($2) . '">' . xml_escape($1) .'</a>' }egrx
|
||||||
|
);
|
||||||
|
$profile->{metadata}[$i] = {
|
||||||
|
key => $key,
|
||||||
|
value => {
|
||||||
|
markdown => $value,
|
||||||
|
html => $html_value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$self->users->set_profile(
|
||||||
|
uid => $user->{id},
|
||||||
|
profile => $profile
|
||||||
|
);
|
||||||
|
$self->redirect_to( '/p/' . $user->{name} );
|
||||||
|
}
|
||||||
|
|
||||||
|
my $profile = $self->users->get_profile( uid => $user->{id} );
|
||||||
|
$self->param( bio => $profile->{bio}{markdown} );
|
||||||
|
for my $i ( 0 .. $#{ $profile->{metadata} } ) {
|
||||||
|
$self->param( "key_$i" => $profile->{metadata}[$i]{key} );
|
||||||
|
$self->param( "value_$i" => $profile->{metadata}[$i]{value}{markdown} );
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->render( 'edit_profile', name => $user->{name} );
|
||||||
|
}
|
||||||
|
|
||||||
sub insight {
|
sub insight {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,30 @@ sub profile {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $profile = $self->users->get_profile( uid => $user->{id} );
|
||||||
|
|
||||||
|
my $my_user;
|
||||||
|
my $relation;
|
||||||
|
my $inverse_relation;
|
||||||
|
my $is_self;
|
||||||
|
if ( $self->is_user_authenticated ) {
|
||||||
|
$my_user = $self->current_user;
|
||||||
|
if ( $my_user->{id} == $user->{id} ) {
|
||||||
|
$is_self = 1;
|
||||||
|
$my_user = undef;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$relation = $self->users->get_relation(
|
||||||
|
subject => $my_user->{id},
|
||||||
|
object => $user->{id}
|
||||||
|
);
|
||||||
|
$inverse_relation = $self->users->get_relation(
|
||||||
|
subject => $user->{id},
|
||||||
|
object => $my_user->{id}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
my $status = $self->get_user_status( $user->{id} );
|
my $status = $self->get_user_status( $user->{id} );
|
||||||
my $visibility;
|
my $visibility;
|
||||||
if ( $status->{checked_in} or $status->{arr_name} ) {
|
if ( $status->{checked_in} or $status->{arr_name} ) {
|
||||||
|
@ -84,7 +108,12 @@ sub profile {
|
||||||
and $self->status_token_ok($status) )
|
and $self->status_token_ok($status) )
|
||||||
or (
|
or (
|
||||||
$visibility eq 'travelynx'
|
$visibility eq 'travelynx'
|
||||||
and ( $self->is_user_authenticated
|
and ( $my_user
|
||||||
|
or $self->status_token_ok($status) )
|
||||||
|
)
|
||||||
|
or (
|
||||||
|
$visibility eq 'followers'
|
||||||
|
and ( ( $relation and $relation eq 'follows' )
|
||||||
or $self->status_token_ok($status) )
|
or $self->status_token_ok($status) )
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -104,7 +133,7 @@ sub profile {
|
||||||
my @journeys;
|
my @journeys;
|
||||||
|
|
||||||
if ( $user->{past_visible} == 2
|
if ( $user->{past_visible} == 2
|
||||||
or ( $user->{past_visible} == 1 and $self->is_user_authenticated ) )
|
or ( $user->{past_visible} == 1 and $my_user ) )
|
||||||
{
|
{
|
||||||
|
|
||||||
my %opt = (
|
my %opt = (
|
||||||
|
@ -122,7 +151,10 @@ sub profile {
|
||||||
if (
|
if (
|
||||||
$user->{default_visibility_str} eq 'public'
|
$user->{default_visibility_str} eq 'public'
|
||||||
or ( $user->{default_visibility_str} eq 'travelynx'
|
or ( $user->{default_visibility_str} eq 'travelynx'
|
||||||
and $self->is_user_authenticated )
|
and $my_user )
|
||||||
|
or ( $user->{default_visibility_str} eq 'followers'
|
||||||
|
and $relation
|
||||||
|
and $relation eq 'follows' )
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
$opt{with_default_visibility} = 1;
|
$opt{with_default_visibility} = 1;
|
||||||
|
@ -131,9 +163,14 @@ sub profile {
|
||||||
$opt{with_default_visibility} = 0;
|
$opt{with_default_visibility} = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $self->is_user_authenticated ) {
|
if ($my_user) {
|
||||||
|
if ( $relation and $relation eq 'follows' ) {
|
||||||
|
$opt{min_visibility} = 'followers';
|
||||||
|
}
|
||||||
|
else {
|
||||||
$opt{min_visibility} = 'travelynx';
|
$opt{min_visibility} = 'travelynx';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
$opt{min_visibility} = 'public';
|
$opt{min_visibility} = 'public';
|
||||||
}
|
}
|
||||||
|
@ -145,7 +182,10 @@ sub profile {
|
||||||
'profile',
|
'profile',
|
||||||
name => $name,
|
name => $name,
|
||||||
uid => $user->{id},
|
uid => $user->{id},
|
||||||
|
bio => $profile->{bio}{html},
|
||||||
|
metadata => $profile->{metadata},
|
||||||
public_level => $user->{public_level},
|
public_level => $user->{public_level},
|
||||||
|
is_self => $is_self,
|
||||||
journey => $status,
|
journey => $status,
|
||||||
journey_visibility => $visibility,
|
journey_visibility => $visibility,
|
||||||
journeys => [@journeys],
|
journeys => [@journeys],
|
||||||
|
|
|
@ -750,6 +750,30 @@ sub update_webhook_status {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub set_profile {
|
||||||
|
my ( $self, %opt ) = @_;
|
||||||
|
|
||||||
|
my $db = $opt{db} // $self->{pg}->db;
|
||||||
|
my $uid = $opt{uid};
|
||||||
|
my $profile = $opt{profile};
|
||||||
|
|
||||||
|
$db->update(
|
||||||
|
'users',
|
||||||
|
{ profile => JSON->new->encode($profile) },
|
||||||
|
{ id => $uid }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_profile {
|
||||||
|
my ( $self, %opt ) = @_;
|
||||||
|
|
||||||
|
my $db = $opt{db} // $self->{pg}->db;
|
||||||
|
my $uid = $opt{uid};
|
||||||
|
|
||||||
|
return $db->select( 'users', ['profile'], { id => $uid } )
|
||||||
|
->expand->hash->{profile};
|
||||||
|
}
|
||||||
|
|
||||||
sub get_relation {
|
sub get_relation {
|
||||||
my ( $self, %opt ) = @_;
|
my ( $self, %opt ) = @_;
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,8 @@
|
||||||
<th scope="row">Sichtbarkeit</th>
|
<th scope="row">Sichtbarkeit</th>
|
||||||
<td>
|
<td>
|
||||||
<a href="/account/privacy"><i class="material-icons">edit</i></a>
|
<a href="/account/privacy"><i class="material-icons">edit</i></a>
|
||||||
<span><i class="material-icons"><%= visibility_icon($acc->{default_visibility_str}) %></i></span>
|
<i class="material-icons"><%= visibility_icon($acc->{default_visibility_str}) %></i>
|
||||||
|
• <a href="/p/<%= $acc->{name} %>">Öffentliches Profil</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
60
templates/edit_profile.html.ep
Normal file
60
templates/edit_profile.html.ep
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12">
|
||||||
|
<h1>Profil bearbeiten</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
%= form_for '/account/profile' => (method => 'POST') => begin
|
||||||
|
%= csrf_field
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content">
|
||||||
|
<span class="card-title"><%= $name %></span>
|
||||||
|
<p>
|
||||||
|
Markdown möglich, maximal 2000 Zeichen.
|
||||||
|
%= text_area 'bio', id => 'bio', class => 'materialize-textarea'
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="card-action">
|
||||||
|
<a href="/p/<%= $name %>" class="waves-effect waves-light btn">
|
||||||
|
Abbrechen
|
||||||
|
</a>
|
||||||
|
<button class="btn waves-effect waves-light right" type="submit" name="action" value="save">
|
||||||
|
Speichern
|
||||||
|
<i class="material-icons right">send</i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12">
|
||||||
|
Metadaten: Markdown-Links im Inhalt erlaubt, jeweils maximal 500 Zeichen
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
% for my $i (0 .. 10) {
|
||||||
|
<div class="row">
|
||||||
|
<div class="input-field col l3 m12 s12">
|
||||||
|
%= text_field "key_$i", id => "key_$i", maxlength => 50
|
||||||
|
<label for="key_<%= $i %>">Attribut</label>
|
||||||
|
</div>
|
||||||
|
<div class="input-field col l9 m12 s12">
|
||||||
|
%= text_field "value_$i", id => "value_$i", maxlength => 500
|
||||||
|
<label for="value_<%= $i %>">Inhalt</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
% }
|
||||||
|
<div class="row center-align">
|
||||||
|
<div class="col s6">
|
||||||
|
<a href="/p/<%= $name %>" class="waves-effect waves-light btn">
|
||||||
|
Abbrechen
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col s6">
|
||||||
|
<button class="btn waves-effect waves-light" type="submit" name="action" value="save">
|
||||||
|
Speichern
|
||||||
|
<i class="material-icons right">send</i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
%= end
|
Loading…
Reference in a new issue