diff options
| -rwxr-xr-x | lib/Travelynx.pm | 2 | ||||
| -rw-r--r-- | lib/Travelynx/Controller/Account.pm | 68 | ||||
| -rwxr-xr-x | lib/Travelynx/Controller/Profile.pm | 56 | ||||
| -rw-r--r-- | lib/Travelynx/Model/Users.pm | 24 | ||||
| -rw-r--r-- | templates/account.html.ep | 3 | ||||
| -rw-r--r-- | templates/edit_profile.html.ep | 60 | 
6 files changed, 204 insertions, 9 deletions
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{ \[ ([^]]+) \]\( ([^)]+) \) }{'<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 {  	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 @@  				<th scope="row">Sichtbarkeit</th>  				<td>  					<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>  			</tr>  			<tr> 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 @@ +<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  | 
