diff --git a/components/Avatar/Avatar.php b/components/Avatar/Avatar.php index 2e3e77f21e..0a97374d3c 100644 --- a/components/Avatar/Avatar.php +++ b/components/Avatar/Avatar.php @@ -34,7 +34,8 @@ class Avatar extends Component { public function onAddRoute($r) { - $r->connect('avatar', '/{gsactor_id<\d+>}/avatar/{size?full}', [Controller\Avatar::class, 'avatar']); + $r->connect('avatar', '/{gsactor_id<\d+>}/avatar/{size?full}', [Controller\Avatar::class, 'avatar_view']); + $r->connect('settings_avatar', '/settings/avatar', [Controller\Avatar::class, 'settings_avatar']); return Event::next; } diff --git a/components/Avatar/Controller/Avatar.php b/components/Avatar/Controller/Avatar.php index d3046cd62c..4ac333ce55 100644 --- a/components/Avatar/Controller/Avatar.php +++ b/components/Avatar/Controller/Avatar.php @@ -22,8 +22,15 @@ namespace Component\Avatar\Controller; use App\Core\Controller; +use App\Core\Form; use App\Core\GSFile as M; +use function App\Core\I18n\_m; +use App\Util\TemporaryFile; use Exception; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; +use Symfony\Component\Form\Extension\Core\Type\FileType; +use Symfony\Component\Form\Extension\Core\Type\HiddenType; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\HttpFoundation\Request; class Avatar extends Controller @@ -31,7 +38,7 @@ class Avatar extends Controller /** * @throws Exception */ - public function avatar(Request $request, int $gsactor_id, string $size) + public function avatar_view(Request $request, int $gsactor_id, string $size) { switch ($size) { case 'full': @@ -41,4 +48,69 @@ class Avatar extends Controller throw new Exception('Not implemented'); } } + + /** + * Local user avatar panel + */ + public function settings_avatar(Request $request) + { + $form = Form::create([ + ['avatar', FileType::class, ['label' => _m('Avatar'), 'help' => _m('You can upload your personal avatar. The maximum file size is 2MB.'), 'multiple' => false, 'required' => false]], + ['remove', CheckboxType::class, ['label' => _m('Remove avatar'), 'help' => _m('Remove your avatar and use the default one')]], + ['hidden', HiddenType::class, []], + ['save', SubmitType::class, ['label' => _m('Submit')]], + ]); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $data = $form->getData(); + if ($data['remove'] == true) { + } else { + $sfile = 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); + $tempfile = new TemporaryFile('avatar'); + $path = $tempfile->getPath(); + file_put_contents($path, $data_user); + $sfile = new SymfonyFile($path); + } 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'); + } + $user = Common::user(); + $gsactor_id = $user->getId(); + $file = GSFile::validateAndStoreAttachment($sfile, Common::config('avatar', 'dir'), $title = null, $is_local = true, $use_unique = $gsactor_id); + $old_file = null; + $avatar = DB::find('avatar', ['gsactor_id' => $gsactor_id]); + // Must get old id before inserting another one + if ($avatar != null) { + $old_file = $avatar->delete(); + } + DB::persist($file); + // Can only get new id after inserting + DB::flush(); + DB::persist(self::create(['gsactor_id' => $gsactor_id, 'attachment_id' => $file->getId()])); + DB::flush(); + // Only delete files if the commit went through + if ($old_file != null) { + @unlink($old_file); + } + } + Event::handle('DeleteCachedAvatar', [$user->getId()]); + } + + return ['_template' => 'settings/avatar.html.twig', 'avatar' => $form->createView()]; + } } diff --git a/src/Controller/UserPanel.php b/src/Controller/UserPanel.php index e3e9f56766..1526a6b4de 100644 --- a/src/Controller/UserPanel.php +++ b/src/Controller/UserPanel.php @@ -38,11 +38,7 @@ namespace App\Controller; use App\Core\DB\DB; use App\Core\Event; use App\Core\Form; -use App\Core\GSFile; use function App\Core\I18n\_m; -use App\Core\Log; -use App\Entity\Avatar; -use App\Util\ClientException; use App\Util\Common; use App\Util\Form\ArrayTransformer; use Doctrine\DBAL\Types\Types; @@ -51,13 +47,10 @@ use Functional as F; use Misd\PhoneNumberBundle\Form\Type\PhoneNumberType; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; -use Symfony\Component\Form\Extension\Core\Type\FileType; -use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Extension\Core\Type\LanguageType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; -use Symfony\Component\HttpFoundation\File\File as SymfonyFile; use Symfony\Component\HttpFoundation\Request; // }}} Imports @@ -108,66 +101,6 @@ class UserPanel extends AbstractController return ['_template' => 'settings/account.html.twig', 'acc' => $form->createView()]; } - /** - * Local user avatar panel - */ - public function avatar(Request $request) - { - $form = Form::create([ - ['avatar', FileType::class, ['label' => _m('Avatar'), 'help' => _m('You can upload your personal avatar. The maximum file size is 2MB.')]], - ['hidden', HiddenType::class, []], - ['save', SubmitType::class, ['label' => _m('Submit')]], - ]); - - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - $data = $form->getData(); - $sfile = 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); - $filename = tempnam('/tmp/', 'avatar'); - file_put_contents($filename, $data_user); - $sfile = new SymfonyFile($filename); - } 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'); - } - $user = Common::user(); - $gsactor_id = $user->getId(); - $file = GSFile::validateAndStoreAttachment($sfile, Common::config('avatar', 'dir'), $title = null, $is_local = true, $use_unique = $gsactor_id); - $old_file = null; - $avatar = DB::find('avatar', ['gsactor_id' => $gsactor_id]); - // Must get old id before inserting another one - if ($avatar != null) { - $old_file = $avatar->delete(); - } - DB::persist($file); - // Can only get new id after inserting - DB::flush(); - DB::persist(Avatar::create(['gsactor_id' => $gsactor_id, 'attachment_id' => $file->getId()])); - DB::flush(); - // Only delete files if the commit went through - if ($old_file != null) { - @unlink($old_file); - } - Event::handle('DeleteCachedAvatar', [$user->getId()]); - } - - return ['_template' => 'settings/avatar.html.twig', 'avatar' => $form->createView()]; - } - /** * Local user notification settings tabbed panel */ diff --git a/src/Routes/Main.php b/src/Routes/Main.php index 04c370502a..37ab68477c 100644 --- a/src/Routes/Main.php +++ b/src/Routes/Main.php @@ -66,7 +66,7 @@ abstract class Main // Settings pages $r->connect('settings', '/settings', RedirectController::class, ['defaults' => ['route' => 'settings_personal_info']]); - foreach (['personal_info', 'avatar', 'notifications', 'account'] as $s) { + foreach (['personal_info', 'notifications', 'account'] as $s) { $r->connect('settings_' . $s, '/settings/' . $s, [C\UserPanel::class, $s]); } }