2020-06-21 23:56:26 +01:00
< ? php
// {{{ License
2020-08-14 23:37:45 +01:00
2020-06-21 23:56:26 +01:00
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
2020-08-14 23:37:45 +01:00
2020-06-21 23:56:26 +01:00
// }}}
/**
* Handle network public feed
*
* @ package GNUsocial
* @ category Controller
*
2020-06-26 23:44:53 +01:00
* @ author Hugo Sales < hugo @ fc . up . pt >
2020-06-30 17:35:38 +01:00
* @ author Eliseu Amaro < eliseu @ fc . up . pt >
2020-06-21 23:56:26 +01:00
* @ copyright 2020 Free Software Foundation , Inc http :// www . fsf . org
* @ license https :// www . gnu . org / licenses / agpl . html GNU AGPL v3 or later
*/
namespace App\Controller ;
2020-07-30 23:47:47 +01:00
// {{{ Imports
use App\Core\DB\DB ;
use App\Core\Event ;
2020-06-26 23:44:53 +01:00
use App\Core\Form ;
2020-06-21 23:56:26 +01:00
use function App\Core\I18n\_m ;
2020-08-07 03:08:55 +01:00
use App\Core\Log ;
2020-08-08 17:10:25 +01:00
use App\Entity\Avatar ;
2020-08-07 03:08:55 +01:00
use App\Entity\File ;
use App\Util\ClientException ;
2020-07-25 18:56:38 +01:00
use App\Util\Common ;
2020-07-27 04:48:35 +01:00
use App\Util\Form\ArrayTransformer ;
2020-08-07 03:08:55 +01:00
use Component\Media\Media ;
2020-07-30 23:47:47 +01:00
use Doctrine\DBAL\Types\Types ;
use Exception ;
use Functional as F ;
2020-07-26 01:02:20 +01:00
use Misd\PhoneNumberBundle\Form\Type\PhoneNumberType ;
2020-07-20 00:22:41 +01:00
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController ;
2020-07-21 22:02:39 +01:00
use Symfony\Component\Form\Extension\Core\Type\CheckboxType ;
2020-07-22 21:58:23 +01:00
use Symfony\Component\Form\Extension\Core\Type\FileType ;
2020-07-28 00:07:31 +01:00
use Symfony\Component\Form\Extension\Core\Type\HiddenType ;
2020-07-26 01:02:20 +01:00
use Symfony\Component\Form\Extension\Core\Type\LanguageType ;
2020-06-21 23:56:26 +01:00
use Symfony\Component\Form\Extension\Core\Type\SubmitType ;
2020-07-20 22:05:10 +01:00
use Symfony\Component\Form\Extension\Core\Type\TextareaType ;
2020-06-21 23:56:26 +01:00
use Symfony\Component\Form\Extension\Core\Type\TextType ;
2020-08-07 03:08:55 +01:00
use Symfony\Component\HttpFoundation\File\File as SymfonyFile ;
2020-06-21 23:56:26 +01:00
use Symfony\Component\HttpFoundation\Request ;
2020-07-30 23:47:47 +01:00
// }}} Imports
2020-07-20 00:22:41 +01:00
class UserPanel extends AbstractController
2020-06-21 23:56:26 +01:00
{
2020-07-26 01:02:20 +01:00
public function personal_info ( Request $request )
2020-06-21 23:56:26 +01:00
{
2020-07-25 18:56:38 +01:00
$user = Common :: user ();
2020-08-13 02:23:22 +01:00
$actor = $user -> getActor ();
$extra = [ 'self_tags' => $actor -> getSelfTags ()];
2020-07-25 18:56:38 +01:00
$form_definition = [
2020-07-27 04:48:35 +01:00
[ 'nickname' , TextType :: class , [ 'label' => _m ( 'Nickname' ), 'required' => true , 'help' => _m ( '1-64 lowercase letters or numbers, no punctuation or spaces.' )]],
[ 'full_name' , TextType :: class , [ 'label' => _m ( 'Full Name' ), 'required' => false , 'help' => _m ( 'A full name is required, if empty it will be set to your nickname.' )]],
[ 'homepage' , TextType :: class , [ 'label' => _m ( 'Homepage' ), 'required' => false , 'help' => _m ( 'URL of your homepage, blog, or profile on another site.' )]],
[ 'bio' , TextareaType :: class , [ 'label' => _m ( 'Bio' ), 'required' => false , 'help' => _m ( 'Describe yourself and your interests.' )]],
[ 'location' , TextType :: class , [ 'label' => _m ( 'Location' ), 'required' => false , 'help' => _m ( 'Where you are, like "City, State (or Region), Country".' )]],
[ 'self_tags' , TextType :: class , [ 'label' => _m ( 'Self Tags' ), 'required' => false , 'transformer' => ArrayTransformer :: class , 'help' => _m ( 'Tags for yourself (letters, numbers, -, ., and _), comma- or space-separated.' )]],
2020-07-25 18:56:38 +01:00
[ 'save' , SubmitType :: class , [ 'label' => _m ( 'Save' )]],
];
2020-07-27 04:48:35 +01:00
$extra_step = function ( $data , $extra_args ) use ( $user ) { $user -> setNickname ( $data [ 'nickname' ]); };
2020-08-13 02:23:22 +01:00
$form = Form :: handle ( $form_definition , $request , $actor , $extra , $extra_step , [[ 'self_tags' => $extra [ 'self_tags' ]]]);
2020-06-21 23:56:26 +01:00
2020-07-25 18:56:38 +01:00
return [ '_template' => 'settings/profile.html.twig' , 'prof' => $form -> createView ()];
2020-06-21 23:56:26 +01:00
}
2020-07-21 22:02:39 +01:00
public function account ( Request $request )
{
2020-07-27 04:48:35 +01:00
$user = Common :: user ();
$form_definition = [
[ 'outgoing_email' , TextType :: class , [ 'label' => _m ( 'Outgoing email' ), 'required' => true , 'help' => _m ( 'Change the email we use to contact you' )]],
[ 'incoming_email' , TextType :: class , [ 'label' => _m ( 'Incoming email' ), 'required' => true , 'help' => _m ( 'Change the email you use to contact us (for posting, for instance)' )]],
[ 'password' , TextType :: class , [ 'label' => _m ( 'Password' ), 'required' => false , 'help' => _m ( 'Change your password' ), 'attr' => [ 'placeholder' => '********' ]]],
[ 'old_password' , TextType :: class , [ 'label' => _m ( 'Old password' ), 'required' => false , 'help' => _m ( 'Enter your old password for verification' ), 'attr' => [ 'placeholder' => '********' ]]],
[ 'language' , LanguageType :: class , [ 'label' => _m ( 'Language' ), 'required' => false , 'help' => _m ( 'Your preferred language' )]],
2020-08-17 00:22:23 +01:00
[ 'phone_number' , PhoneNumberType :: class , [ 'label' => _m ( 'Phone number' ), 'required' => false , 'help' => _m ( 'Your phone number' ), 'data_class' => null ]],
2020-07-26 01:02:20 +01:00
[ 'save' , SubmitType :: class , [ 'label' => _m ( 'Save' )]],
2020-07-27 04:48:35 +01:00
];
$form = Form :: handle ( $form_definition , $request , $user );
2020-07-21 22:02:39 +01:00
2020-07-27 04:48:35 +01:00
return [ '_template' => 'settings/account.html.twig' , 'acc' => $form -> createView ()];
2020-07-21 22:02:39 +01:00
}
2020-07-22 21:58:23 +01:00
public function avatar ( Request $request )
{
2020-08-08 17:10:25 +01:00
$form = Form :: create ([
2020-08-07 03:08:55 +01:00
[ 'avatar' , FileType :: class , [ 'label' => _m ( 'Avatar' ), 'help' => _m ( 'You can upload your personal avatar. The maximum file size is 2MB.' )]],
2020-07-28 00:07:31 +01:00
[ 'hidden' , HiddenType :: class , []],
2020-08-07 03:08:55 +01:00
[ 'save' , SubmitType :: class , [ 'label' => _m ( 'Submit' )]],
2020-07-26 01:27:08 +01:00
]);
2020-07-22 21:58:23 +01:00
2020-08-08 17:10:25 +01:00
$form -> handleRequest ( $request );
2020-07-28 00:07:31 +01:00
2020-08-08 17:10:25 +01:00
if ( $form -> isSubmitted () && $form -> isValid ()) {
$data = $form -> getData ();
2020-08-07 03:08:55 +01:00
$sfile = $file_title = null ;
if ( isset ( $data [ 'hidden' ])) {
// Cropped client side
$matches = [];
if ( ! empty ( preg_match ( '/data:([^;]*)(;(base64))?,(.*)/' , $data [ 'hidden' ], $matches ))) {
list (, $mimetype_user , , $encoding_user , $data_user ) = $matches ;
if ( $encoding_user == 'base64' ) {
$data_user = base64_decode ( $data_user );
2020-08-08 17:10:25 +01:00
$filename = tempnam ( '/tmp/' , 'avatar' );
file_put_contents ( $filename , $data_user );
2020-08-07 03:08:55 +01:00
try {
2020-08-08 17:10:25 +01:00
$sfile = new SymfonyFile ( $filename );
2020-08-07 03:08:55 +01:00
$file_title = $data [ 'avatar' ] -> getFilename ();
} finally {
2020-08-08 17:10:25 +01:00
// fclose($tmp_file);
2020-08-07 03:08:55 +01:00
}
} else {
Log :: info ( 'Avatar upload got an invalid encoding, something\'s fishy and/or wrong' );
}
}
} elseif ( isset ( $data [ 'avatar' ])) {
// Cropping failed (e.g. disabled js), have file as uploaded
$sfile = $data [ 'avatar' ];
} else {
throw new ClientException ( 'Invalid form' );
}
2020-08-14 23:37:45 +01:00
$actor = Common :: actor ();
$actor_id = $actor -> getId ();
$file = Media :: validateAndStoreFile ( $sfile , Common :: config ( 'avatar' , 'dir' ), $file_title , $is_local = true , $use_unique = $actor_id );
$old_file = null ;
$avatar = DB :: find ( 'avatar' , [ 'gsactor_id' => $actor_id ]);
// Must get old id before inserting another one
2020-08-08 17:10:25 +01:00
if ( $avatar != null ) {
2020-08-14 23:37:45 +01:00
$old_file = $avatar -> delete ();
2020-08-08 17:10:25 +01:00
}
2020-08-14 23:37:45 +01:00
DB :: persist ( $file );
// Can only get new id after inserting
DB :: flush ();
DB :: persist ( Avatar :: create ([ 'gsactor_id' => $actor_id , 'file_id' => $file -> getId ()]));
2020-08-08 17:10:25 +01:00
DB :: flush ();
// Only delete files if the commit went through
2020-08-14 23:37:45 +01:00
if ( $old_file != null ) {
@ unlink ( $old_file );
}
2020-08-19 16:32:45 +01:00
Event :: handle ( 'delete_cached_avatar' , [ $actor -> getNickname ()]);
2020-07-28 00:07:31 +01:00
}
2020-08-08 17:10:25 +01:00
return [ '_template' => 'settings/avatar.html.twig' , 'avatar' => $form -> createView ()];
2020-07-22 21:58:23 +01:00
}
2020-07-26 16:05:07 +01:00
public function notifications ( Request $request )
2020-07-22 21:58:23 +01:00
{
2020-07-30 23:47:47 +01:00
$schema = DB :: getConnection () -> getSchemaManager ();
$platform = $schema -> getDatabasePlatform ();
2020-08-05 17:31:39 +01:00
$columns = Common :: arrayRemoveKeys ( $schema -> listTableColumns ( 'user_notification_prefs' ), [ 'user_id' , 'transport' , 'created' , 'modified' ]);
2020-07-30 23:47:47 +01:00
$form_defs = [ 'placeholder' => []];
foreach ( $columns as $name => $col ) {
2020-08-05 17:31:39 +01:00
$type = $col -> getType ();
$val = $type -> convertToPHPValue ( $col -> getDefault (), $platform );
$type_str = lcfirst ( substr (( string ) $type , 1 ));
$label = str_replace ( '_' , ' ' , ucfirst ( $name ));
2020-08-06 00:05:06 +01:00
$labels = [
2020-08-13 02:23:22 +01:00
'target_actor_id' => 'Target Actors' ,
'dm' => 'DM' ,
2020-08-06 00:05:06 +01:00
];
$help = [
2020-08-13 02:23:22 +01:00
'target_actor_id' => 'If specified, these settings apply only to these profiles (comma- or space-separated list)' ,
2020-08-06 00:05:06 +01:00
'activity_by_followed' => 'Notify me when someone I follow has new activity' ,
'mention' => 'Notify me when mentions me in a notice' ,
'reply' => 'Notify me when someone replies to a notice made by me' ,
'follow' => 'Notify me when someone follows me or asks for permission to do so' ,
'favorite' => 'Notify me when someone favorites one of my notices' ,
'nudge' => 'Notify me when someone nudges me' ,
'dm' => 'Notify me when someone sends me a direct message' ,
'post_on_status_change' => 'Post a notice when my status in this service changes' ,
'enable_posting' => 'Enable posting from this service' ,
];
2020-08-05 17:31:39 +01:00
switch ( $type_str ) {
2020-07-30 23:47:47 +01:00
case Types :: BOOLEAN :
2020-08-06 00:05:06 +01:00
$form_defs [ 'placeholder' ][ $name ] = [ $name , CheckboxType :: class , [ 'data' => $val , 'label' => _m ( $labels [ $name ] ? ? $label ), 'help' => _m ( $help [ $name ])]];
2020-07-30 23:47:47 +01:00
break ;
case Types :: INTEGER :
2020-08-13 02:23:22 +01:00
if ( $name == 'target_actor_id' ) {
$form_defs [ 'placeholder' ][ $name ] = [ 'target_actors' , TextType :: class , [ 'data' => $val , 'label' => _m ( $labels [ $name ]), 'help' => _m ( $help [ $name ])], 'transformer' => ActorArrayTransformer :: class ];
2020-07-30 23:47:47 +01:00
break ;
}
// no break
default :
throw new Exception ( " Structure of table user_notification_prefs changed in a way not accounted to in notification settings ( { $name } ) " , 500 );
}
}
2020-08-05 17:31:39 +01:00
Event :: handle ( 'add_notification_transport' , [ & $form_defs ]);
2020-07-30 23:47:47 +01:00
unset ( $form_defs [ 'placeholder' ]);
$tabbed_forms = [];
foreach ( $form_defs as $transport_name => $f ) {
$tabbed_forms [ $transport_name ] = Form :: create ( $f );
}
$tabbed_forms = F\map ( $tabbed_forms , function ( $f ) { return $f -> createView (); });
2020-08-06 00:05:06 +01:00
return [
'_template' => 'settings/notifications.html.twig' ,
'tabbed_forms' => $tabbed_forms ,
];
2020-07-22 21:58:23 +01:00
}
2020-07-23 15:08:31 +01:00
}