2020-06-21 23:56:26 +01:00
< ? php
2021-12-04 19:58:00 +00:00
declare ( strict_types = 1 );
2021-10-10 09:26:18 +01:00
2020-06-21 23:56:26 +01:00
// {{{ 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
*
2021-02-19 23:29:43 +00:00
* @ author Hugo Sales < hugo @ hsal . es >
2020-06-30 17:35:38 +01:00
* @ author Eliseu Amaro < eliseu @ fc . up . pt >
2021-02-19 23:29:43 +00:00
* @ copyright 2020 - 2021 Free Software Foundation , Inc http :// www . fsf . org
2020-06-21 23:56:26 +01:00
* @ 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
2021-11-15 13:34:34 +00:00
use App\Core\Controller ;
2020-07-30 23:47:47 +01:00
use App\Core\DB\DB ;
use App\Core\Event ;
2020-06-26 23:44:53 +01:00
use App\Core\Form ;
2021-12-04 19:58:00 +00:00
use function App\Core\I18n\_m ;
2021-10-21 13:44:30 +01:00
use App\Core\Log ;
2020-07-25 18:56:38 +01:00
use App\Util\Common ;
2021-08-04 21:11:01 +01:00
use App\Util\Exception\AuthenticationException ;
2021-12-03 02:51:41 +00:00
use App\Util\Exception\NoLoggedInUser ;
2021-08-04 21:11:01 +01:00
use App\Util\Exception\ServerException ;
2021-09-06 19:49:03 +01:00
use App\Util\Form\ActorArrayTransformer ;
2021-12-23 17:10:56 +00:00
use App\Util\Form\ActorForms ;
2021-08-06 12:12:10 +01:00
use App\Util\Form\FormFields ;
use App\Util\Formatting ;
2021-12-21 19:04:38 +00:00
use Component\Language\Controller\Language as LanguageController ;
2021-11-27 04:11:35 +00:00
use Component\Notification\Entity\UserNotificationPrefs ;
2020-07-30 23:47:47 +01:00
use Doctrine\DBAL\Types\Types ;
use Exception ;
2020-07-21 22:02:39 +01:00
use Symfony\Component\Form\Extension\Core\Type\CheckboxType ;
2021-12-03 02:51:41 +00:00
use Symfony\Component\Form\Extension\Core\Type\PasswordType ;
2020-06-21 23:56:26 +01:00
use Symfony\Component\Form\Extension\Core\Type\SubmitType ;
use Symfony\Component\Form\Extension\Core\Type\TextType ;
2021-12-03 02:51:41 +00:00
use Symfony\Component\Form\FormInterface ;
2020-06-21 23:56:26 +01:00
use Symfony\Component\HttpFoundation\Request ;
2020-07-30 23:47:47 +01:00
// }}} Imports
2021-11-15 13:34:34 +00:00
class UserPanel extends Controller
2020-06-21 23:56:26 +01:00
{
2021-07-29 17:29:25 +01:00
/**
* Return main settings page forms
*
2022-01-12 19:05:29 +00:00
* @ throws \App\Util\Exception\NicknameEmptyException
* @ throws \App\Util\Exception\NicknameInvalidException
* @ throws \App\Util\Exception\NicknameNotAllowedException
* @ throws \App\Util\Exception\NicknameTakenException
* @ throws \App\Util\Exception\NicknameTooLongException
* @ throws \App\Util\Exception\RedirectException
* @ throws \Doctrine\DBAL\Exception
* @ throws AuthenticationException
* @ throws NoLoggedInUser
* @ throws ServerException
2021-07-29 17:29:25 +01:00
*/
2021-12-21 19:04:38 +00:00
public function allSettings ( Request $request , LanguageController $language ) : array
2021-07-29 17:29:25 +01:00
{
2021-12-23 17:10:56 +00:00
// Ensure the user is logged in and retrieve Actor object for given user
$user = Common :: ensureLoggedIn ();
$actor = $user -> getActor ();
2021-12-03 02:51:41 +00:00
2022-01-10 11:29:37 +00:00
$personal_form = ActorForms :: personalInfo ( $request , $actor , $user );
2021-12-23 17:10:56 +00:00
$email_form = self :: email ( $request );
$password_form = self :: password ( $request );
$notifications_form_array = self :: notifications ( $request );
$language_form = $language -> settings ( $request );
2021-07-29 17:29:25 +01:00
2021-08-04 21:11:01 +01:00
return [
2021-12-04 19:58:00 +00:00
'_template' => 'settings/base.html.twig' ,
2021-12-08 17:53:29 +00:00
'personal_info_form' => $personal_form -> createView (),
'email_form' => $email_form -> createView (),
'password_form' => $password_form -> createView (),
'language_form' => $language_form -> createView (),
2021-08-04 21:11:01 +01:00
'tabbed_forms_notify' => $notifications_form_array ,
2021-12-04 19:58:00 +00:00
'open_details_query' => $this -> string ( 'open' ),
2021-07-29 17:29:25 +01:00
];
}
2020-11-06 19:47:15 +00:00
/**
2021-12-03 02:51:41 +00:00
* Change email settings form
*
* @ throws NoLoggedInUser
* @ throws ServerException
2020-11-06 19:47:15 +00:00
*/
2021-12-23 17:10:56 +00:00
private static function email ( Request $request ) : FormInterface
2020-06-21 23:56:26 +01:00
{
2021-12-03 02:51:41 +00:00
$user = Common :: ensureLoggedIn ();
// TODO Add support missing settings
$form = Form :: create ([
[ 'outgoing_email' , TextType :: class , [ 'label' => _m ( 'Outgoing email' ), 'required' => false , 'help' => _m ( 'Change the email we use to contact you' )]],
[ 'incoming_email' , TextType :: class , [ 'label' => _m ( 'Incoming email' ), 'required' => false , 'help' => _m ( 'Change the email you use to contact us (for posting, for instance)' )]],
[ 'save_email' , SubmitType :: class , [ 'label' => _m ( 'Save email info' )]],
]);
$form -> handleRequest ( $request );
if ( $form -> isSubmitted () && $form -> isValid ()) {
$data = $form -> getData ();
foreach ( $data as $key => $val ) {
$method = 'set' . ucfirst ( Formatting :: snakeCaseToCamelCase ( $key ));
if ( method_exists ( $user , $method )) {
$user -> { $method }( $val );
}
2021-12-02 21:26:17 +00:00
}
2021-12-03 02:51:41 +00:00
DB :: flush ();
}
return $form ;
2020-06-21 23:56:26 +01:00
}
2020-07-21 22:02:39 +01:00
2020-11-06 19:47:15 +00:00
/**
2021-12-03 02:51:41 +00:00
* Change password form
*
* @ throws AuthenticationException
* @ throws NoLoggedInUser
* @ throws ServerException
2020-11-06 19:47:15 +00:00
*/
2021-12-23 17:10:56 +00:00
private static function password ( Request $request ) : FormInterface
2020-07-21 22:02:39 +01:00
{
2021-11-15 17:00:58 +00:00
$user = Common :: ensureLoggedIn ();
2021-08-04 21:11:01 +01:00
// TODO Add support missing settings
2021-11-15 17:00:58 +00:00
2021-08-04 21:11:01 +01:00
$form = Form :: create ([
2021-12-03 02:51:41 +00:00
[ 'old_password' , PasswordType :: class , [ 'label' => _m ( 'Old password' ), 'required' => true , 'help' => _m ( 'Enter your old password for verification' ), 'attr' => [ 'placeholder' => '********' ]]],
FormFields :: repeated_password ([ 'required' => true ]),
[ 'save_password' , SubmitType :: class , [ 'label' => _m ( 'Save new password' )]],
2021-08-04 21:11:01 +01:00
]);
$form -> handleRequest ( $request );
if ( $form -> isSubmitted () && $form -> isValid ()) {
$data = $form -> getData ();
2021-12-04 19:58:00 +00:00
if ( ! \is_null ( $data [ 'old_password' ])) {
2021-08-04 21:11:01 +01:00
$data [ 'password' ] = $form -> get ( 'password' ) -> getData ();
if ( ! ( $user -> changePassword ( $data [ 'old_password' ], $data [ 'password' ]))) {
throw new AuthenticationException ( _m ( 'The provided password is incorrect' ));
}
}
unset ( $data [ 'old_password' ], $data [ 'password' ]);
2021-12-03 02:51:41 +00:00
foreach ( $data as $key => $val ) {
$method = 'set' . ucfirst ( Formatting :: snakeCaseToCamelCase ( $key ));
if ( method_exists ( $user , $method )) {
$user -> { $method }( $val );
}
}
DB :: flush ();
}
return $form ;
}
2020-11-06 19:47:15 +00:00
/**
* Local user notification settings tabbed panel
2022-01-12 19:05:29 +00:00
*
* @ throws \Doctrine\DBAL\Exception
* @ throws NoLoggedInUser
* @ throws ServerException
2020-11-06 19:47:15 +00:00
*/
2021-12-23 17:10:56 +00:00
private static function notifications ( Request $request ) : array
2020-07-22 21:58:23 +01:00
{
2021-12-04 19:58:00 +00:00
$user = Common :: ensureLoggedIn ();
$schema = DB :: getConnection () -> getSchemaManager ();
$platform = $schema -> getDatabasePlatform ();
$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 ) {
2022-01-12 19:05:29 +00:00
$type = $col -> getType ();
// TODO: current value is never retrieved properly, form always gets defaults
2021-12-04 19:58:00 +00:00
$val = $type -> convertToPHPValue ( $col -> getDefault (), $platform );
2021-11-15 13:37:29 +00:00
$type_str = $type -> getName ();
2021-12-04 19:58:00 +00:00
$label = str_replace ( '_' , ' ' , ucfirst ( $name ));
2020-08-06 00:05:06 +01:00
$labels = [
2021-09-18 03:22:27 +01:00
'target_actor_id' => 'Target Actors' ,
2021-12-04 19:58:00 +00:00
'dm' => 'DM' ,
2020-08-06 00:05:06 +01:00
];
$help = [
2021-12-04 19:58:00 +00:00
'target_actor_id' => 'If specified, these settings apply only to these profiles (comma- or space-separated list)' ,
2021-11-08 13:44:35 +00:00
'activity_by_subscribed' => 'Notify me when someone I subscribed has new activity' ,
2021-12-04 19:58:00 +00:00
'mention' => 'Notify me when mentions me in a notice' ,
'reply' => 'Notify me when someone replies to a notice made by me' ,
'subscription' => 'Notify me when someone subscribes to 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-06 00:05:06 +01:00
];
2020-08-05 17:31:39 +01:00
switch ( $type_str ) {
2021-12-03 02:51:41 +00:00
case Types :: BOOLEAN :
$form_defs [ 'placeholder' ][ $name ] = [ $name , CheckboxType :: class , [ 'data' => $val , 'required' => false , 'label' => _m ( $labels [ $name ] ? ? $label ), 'help' => _m ( $help [ $name ])]];
break ;
case Types :: INTEGER :
2022-01-12 19:05:29 +00:00
if ( $name === 'target_actor_id' ) {
2021-12-03 02:51:41 +00:00
$form_defs [ 'placeholder' ][ $name ] = [ $name , TextType :: class , [ 'data' => $val , 'required' => false , 'label' => _m ( $labels [ $name ]), 'help' => _m ( $help [ $name ])], 'transformer' => ActorArrayTransformer :: class ];
}
break ;
default :
// @codeCoverageIgnoreStart
Log :: critical ( " Structure of table user_notification_prefs changed in a way not accounted to in notification settings ( { $name } ): " . $type_str );
throw new ServerException ( _m ( 'Internal server error' ));
2021-08-04 21:11:01 +01:00
// @codeCoverageIgnoreEnd
2020-07-30 23:47:47 +01:00
}
}
2021-12-04 19:58:00 +00:00
$form_defs [ 'placeholder' ][ 'save' ] = fn ( string $transport , string $form_name ) => [ $form_name , SubmitType :: class ,
[ 'label' => _m ( 'Save notification settings for {transport}' , [ 'transport' => $transport ])], ];
2021-08-07 19:24:11 +01:00
2020-10-19 19:22:59 +01:00
Event :: handle ( 'AddNotificationTransport' , [ & $form_defs ]);
2020-07-30 23:47:47 +01:00
unset ( $form_defs [ 'placeholder' ]);
$tabbed_forms = [];
2021-12-26 22:17:26 +00:00
foreach ( $form_defs as $transport_name => $f ) { // @phpstan-ignore-line
2021-08-07 19:24:11 +01:00
unset ( $f [ 'save' ]);
2021-12-08 17:53:29 +00:00
$form = Form :: create ( $f );
$tabbed_forms [ $transport_name ][ 'title' ] = $transport_name ;
$tabbed_forms [ $transport_name ][ 'desc' ] = _m ( '{transport} notification settings' , [ 'transport' => $transport_name ]);
$tabbed_forms [ $transport_name ][ 'id' ] = " settings-notifications- { $transport_name } " ;
$tabbed_forms [ $transport_name ][ 'form' ] = $form -> createView ();
2021-08-07 19:24:11 +01:00
$form -> handleRequest ( $request );
2022-01-12 19:05:29 +00:00
// TODO: on submit, form reports a nonce error. Therefore, user changes are not applied
// errors: array:1 [▼
// 0 => Symfony\Component\Form\FormError {#2956 ▼
// #messageTemplate: "Invalid nonce"
// #messageParameters: []
// #messagePluralization: null
// -message: "Invalid nonce"
// -cause: Symfony\Component\Security\Csrf\CsrfToken {#2955 ▶}
// -origin: Symfony\Component\Form\Form {#2868}
// }
// ]
if ( $form -> isSubmitted ()) {
2021-08-07 19:24:11 +01:00
$data = $form -> getData ();
2021-08-07 19:25:10 +01:00
unset ( $data [ 'translation_domain' ]);
2021-08-04 21:11:01 +01:00
try {
2022-01-12 19:05:29 +00:00
[ $entity , $is_update ] = UserNotificationPrefs :: createOrUpdate (
2021-08-04 21:11:01 +01:00
array_merge ([ 'user_id' => $user -> getId (), 'transport' => $transport_name ], $data ),
2021-10-10 09:26:18 +01:00
find_by_keys : [ 'user_id' , 'transport' ],
2021-08-04 21:11:01 +01:00
);
if ( ! $is_update ) {
2022-01-12 19:05:29 +00:00
DB :: persist ( $entity );
2021-08-04 21:11:01 +01:00
}
DB :: flush ();
// @codeCoverageIgnoreStart
2021-10-10 09:26:18 +01:00
} catch ( Exception $e ) {
2021-08-04 21:11:01 +01:00
// Somehow, the exception doesn't bubble up in phpunit
2021-10-21 13:44:30 +01:00
// dd($data, $e);
2021-08-04 21:11:01 +01:00
// @codeCoverageIgnoreEnd
2021-10-21 13:44:30 +01:00
Log :: critical ( 'Exception at ' . $e -> getFile () . ':' . $e -> getLine () . ': ' . $e -> getMessage ());
2021-08-07 19:25:10 +01:00
}
2021-08-07 19:24:11 +01:00
}
2020-07-30 23:47:47 +01:00
}
2021-07-29 17:29:25 +01:00
return $tabbed_forms ;
2020-07-22 21:58:23 +01:00
}
2020-07-23 15:08:31 +01:00
}