From 0172f0ce8ac543d287fef02786960d3e980556f4 Mon Sep 17 00:00:00 2001 From: Derf Null Date: Sun, 4 Jun 2023 14:28:04 +0200 Subject: [PATCH] add profile editor --- lib/Travelynx.pm | 2 + lib/Travelynx/Controller/Account.pm | 68 +++++++++++++++++++++++++++++ lib/Travelynx/Controller/Profile.pm | 56 ++++++++++++++++++++---- lib/Travelynx/Model/Users.pm | 24 ++++++++++ templates/account.html.ep | 3 +- templates/edit_profile.html.ep | 60 +++++++++++++++++++++++++ 6 files changed, 204 insertions(+), 9 deletions(-) create mode 100644 templates/edit_profile.html.ep diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm index 2688556..adb6132 100755 --- a/lib/Travelynx.pm +++ b/lib/Travelynx.pm @@ -2213,6 +2213,7 @@ sub startup { $authed_r->get('/account')->to('account#account'); $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/traewelling')->to('traewelling#settings'); $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('/confirm_mail/:token')->to('account#confirm_mail'); $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/traewelling')->to('traewelling#settings'); $authed_r->post('/account/insight')->to('account#insight'); diff --git a/lib/Travelynx/Controller/Account.pm b/lib/Travelynx/Controller/Account.pm index 1fa762a..af97c96 100644 --- a/lib/Travelynx/Controller/Account.pm +++ b/lib/Travelynx/Controller/Account.pm @@ -7,6 +7,8 @@ use Mojo::Base 'Mojolicious::Controller'; use Crypt::Eksblowfish::Bcrypt qw(bcrypt en_base64); use JSON; +use Mojo::Util qw(xml_escape); +use Text::Markdown; use UUID::Tiny qw(:std); 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{ \[ ([^]]+) \]\( ([^)]+) \) }{'' . xml_escape($1) .'' }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 { my ($self) = @_; diff --git a/lib/Travelynx/Controller/Profile.pm b/lib/Travelynx/Controller/Profile.pm index 1660a5b..86b8922 100755 --- a/lib/Travelynx/Controller/Profile.pm +++ b/lib/Travelynx/Controller/Profile.pm @@ -70,6 +70,30 @@ sub profile { 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 $visibility; if ( $status->{checked_in} or $status->{arr_name} ) { @@ -84,7 +108,12 @@ sub profile { and $self->status_token_ok($status) ) or ( $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) ) ) ) @@ -104,7 +133,7 @@ sub profile { my @journeys; 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 = ( @@ -122,7 +151,10 @@ sub profile { if ( $user->{default_visibility_str} eq 'public' 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; @@ -131,8 +163,13 @@ sub profile { $opt{with_default_visibility} = 0; } - if ( $self->is_user_authenticated ) { - $opt{min_visibility} = 'travelynx'; + if ($my_user) { + if ( $relation and $relation eq 'follows' ) { + $opt{min_visibility} = 'followers'; + } + else { + $opt{min_visibility} = 'travelynx'; + } } else { $opt{min_visibility} = 'public'; @@ -143,9 +180,12 @@ sub profile { $self->render( 'profile', - name => $name, - uid => $user->{id}, - public_level => $user->{public_level}, + name => $name, + uid => $user->{id}, + bio => $profile->{bio}{html}, + metadata => $profile->{metadata}, + public_level => $user->{public_level}, + is_self => $is_self, journey => $status, journey_visibility => $visibility, journeys => [@journeys], diff --git a/lib/Travelynx/Model/Users.pm b/lib/Travelynx/Model/Users.pm index fbd53fd..7326e34 100644 --- a/lib/Travelynx/Model/Users.pm +++ b/lib/Travelynx/Model/Users.pm @@ -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 { my ( $self, %opt ) = @_; diff --git a/templates/account.html.ep b/templates/account.html.ep index e5dba60..ef6b847 100644 --- a/templates/account.html.ep +++ b/templates/account.html.ep @@ -73,7 +73,8 @@ Sichtbarkeit edit - <%= visibility_icon($acc->{default_visibility_str}) %> + <%= visibility_icon($acc->{default_visibility_str}) %> + • Öffentliches Profil diff --git a/templates/edit_profile.html.ep b/templates/edit_profile.html.ep new file mode 100644 index 0000000..55b1e1e --- /dev/null +++ b/templates/edit_profile.html.ep @@ -0,0 +1,60 @@ +
+
+

Profil bearbeiten

+
+
+%= form_for '/account/profile' => (method => 'POST') => begin + %= csrf_field +
+
+
+
+ <%= $name %> +

+ Markdown möglich, maximal 2000 Zeichen. + %= text_area 'bio', id => 'bio', class => 'materialize-textarea' +

+
+
+ + Abbrechen + + +
+
+
+
+
+
+ Metadaten: Markdown-Links im Inhalt erlaubt, jeweils maximal 500 Zeichen +
+
+ % for my $i (0 .. 10) { +
+
+ %= text_field "key_$i", id => "key_$i", maxlength => 50 + +
+
+ %= text_field "value_$i", id => "value_$i", maxlength => 500 + +
+
+ % } +
+ +
+ +
+
+%= end