From 2bf914f96ff469505cf2ecb4731cd849df9befad Mon Sep 17 00:00:00 2001 From: Hugo Sales Date: Fri, 7 Aug 2020 02:08:55 +0000 Subject: [PATCH] [AVATAR] Handle avatar upload without js and save and validate uploaded files --- src/Controller/UserPanel.php | 56 +++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/src/Controller/UserPanel.php b/src/Controller/UserPanel.php index 16674d26a6..2c300e748d 100644 --- a/src/Controller/UserPanel.php +++ b/src/Controller/UserPanel.php @@ -1,7 +1,6 @@ . - // }}} /** @@ -39,8 +37,12 @@ use App\Core\DB\DB; use App\Core\Event; use App\Core\Form; use function App\Core\I18n\_m; +use App\Core\Log; +use App\Entity\File; +use App\Util\ClientException; use App\Util\Common; use App\Util\Form\ArrayTransformer; +use Component\Media\Media; use Doctrine\DBAL\Types\Types; use Exception; use Functional as F; @@ -53,6 +55,7 @@ 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 @@ -100,20 +103,53 @@ class UserPanel extends AbstractController public function avatar(Request $request) { $avatar = Form::create([ - ['avatar', FileType::class, ['label' => _m('Avatar'), 'help' => _m('You can upload your personal avatar. The maximum file size is 2MB.')]], + ['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')]], + ['save', SubmitType::class, ['label' => _m('Submit')]], ]); $avatar->handleRequest($request); if ($avatar->isSubmitted() && $avatar->isValid()) { - $data = $avatar->getData()['hidden']; - list($type, $data) = explode(';', $data); - list(, $data) = explode(',', $data); - $data = base64_decode($data); - - file_put_contents('/tmp/image.png', $data); + $data = $avatar->getData(); + $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); + $tmp_file = tmpfile(); + fwrite($tmp_file, $data_user); + try { + $sfile = new SymfonyFile(stream_get_meta_data($tmp_file)['uri']); + $file_title = $data['avatar']->getFilename(); + } finally { + fclose($tmp_file); + } + } 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'); + } + try { + $profile_id = Common::user()->getProfile()->getId(); + $file = Media::validateAndStoreFile($sfile, Common::config('avatar', 'dir'), $file_title); + $fs_files_to_delete = DB::find('avatar', ['profile_id' => $profile_id])->delete(); + DB::persist(Avatar::create(['profile_id' => $profile_id, 'file_id' => $file->getId()])); + DB::persist($file); + DB::flush(); + // Only delete files if the commit went through + File::deleteFiles($fs_files_to_delete); + } catch (Exception $e) { + throw $e; + } } return ['_template' => 'settings/avatar.html.twig', 'avatar' => $avatar->createView()];