Skip to content

Commit

Permalink
Feature/avatar upload (#166)
Browse files Browse the repository at this point in the history
* Upload avatar.
* Show avatar.
* Store uploaded image in database. Add twig filter to convert to base64 string.
  • Loading branch information
Schnoop committed Sep 23, 2023
1 parent fb3b777 commit 5465bb6
Show file tree
Hide file tree
Showing 14 changed files with 316 additions and 9 deletions.
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"require": {
"php": "^8.1",
"ext-gd": "*",
"ext-json": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-imagick": "*",
"doctrine/common": "^3.4",
"doctrine/doctrine-bundle": "^2.9",
"doctrine/doctrine-migrations-bundle": "^3.2",
Expand All @@ -25,6 +26,7 @@
"symfony/security-bundle": "^6.3",
"symfony/translation": "^6.3",
"symfony/twig-bundle": "^6.3",
"symfony/validator": "6.3.*",
"symfony/yaml": "^6.3",
"twig/twig": "^3.6"
},
Expand Down
101 changes: 99 additions & 2 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions config/doctrine/User.orm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
</id>
<field name="username" column="username" length="180" unique="true"/>
<field name="email" column="email" length="180" unique="true"/>
<field name="avatar" column="avatar" type="blob" />
<field name="enabled" type="boolean" column="enabled">
<options>
<option name="default">0</option>
Expand Down
8 changes: 8 additions & 0 deletions config/routes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,14 @@ Game/Account/Edit:
path: /game/account/edit
controller: FrankProjects\UltimateWarfare\Controller\Game\UserController::edit

Game/Account/UploadAvatar:
path: /game/account/upload_avatar
controller: FrankProjects\UltimateWarfare\Controller\Game\UserController::uploadAvatar

Game/Account/DeleteAvatar:
path: /game/account/delete_avatar
controller: FrankProjects\UltimateWarfare\Controller\Game\UserController::deleteAvatar

Game/Banned:
path: /game/banned
controller: FrankProjects\UltimateWarfare\Controller\Game\UserController::banned
Expand Down
1 change: 1 addition & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
app.gg_recaptcha_site_key: '%env(GOOGLE_RECAPTCHA_SITE_KEY)%'
app.avatars_directory: '%kernel.project_dir%/var/uploads/avatars'

services:
# default configuration for services in *this* file
Expand Down
64 changes: 63 additions & 1 deletion src/Controller/Game/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use FrankProjects\UltimateWarfare\Entity\UnbanRequest;
use FrankProjects\UltimateWarfare\Form\ChangePasswordType;
use FrankProjects\UltimateWarfare\Form\UploadAvatarType;
use FrankProjects\UltimateWarfare\Repository\UnbanRequestRepository;
use FrankProjects\UltimateWarfare\Repository\UserRepository;
use Symfony\Component\HttpFoundation\Request;
Expand Down Expand Up @@ -104,11 +105,72 @@ public function edit(Request $request, UserPasswordHasherInterface $passwordHash
[
'user' => $this->getGameUser(),
'userType' => $this->getAccountType(),
'changePasswordForm' => $changePasswordForm->createView()
'changePasswordForm' => $changePasswordForm->createView(),
'uploadAvatar' => $this->createForm(UploadAvatarType::class)->createView()
]
);
}

public function uploadAvatar(Request $request): Response
{
$user = $this->getGameUser();
$form = $this->createForm(UploadAvatarType::class);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$avatar = $form->get('avatar')->getData();

if ($avatar !== null) {
$uploadedFile = $user->getId() . '-' . uniqid('', true) . '.' . $avatar->guessExtension();

try {
$avatar->move(
$this->getParameter('app.avatars_directory'),
$uploadedFile
);

$image = new \Imagick($this->getParameter('app.avatars_directory') . '/' . $uploadedFile);
$image->setImageFormat('png');
$image->setImageBackgroundColor('transparent');
$image->setImageAlphaChannel(\Imagick::ALPHACHANNEL_OPAQUE);
$image->cropThumbnailImage(200, 200);
$image->roundCornersImage(100, 100);

unlink($this->getParameter('app.avatars_directory') . '/' . $uploadedFile);

$user->setAvatar($image->getImageBlob());
$this->userRepository->save($user);

$this->addFlash('success', 'Avatar uploaded successfully!');
} catch (\Exception $e) {
$this->addFlash('error', 'Could not upload avatar');
}

return $this->redirectToRoute('Game/Account/Edit');
}
}

return $this->render(
'game/editAccount.html.twig',
[
'user' => $this->getGameUser(),
'userType' => $this->getAccountType(),
'changePasswordForm' => $this->createForm(ChangePasswordType::class)->createView(),
'uploadAvatar' => $form->createView()
]
);
}

public function deleteAvatar(): Response
{
$user = $this->getGameUser();
$user->setAvatar('');
$this->userRepository->save($user);

$this->addFlash('success', 'Avatar successfully deleted!');
return $this->redirectToRoute('Game/Account/Edit');
}

private function getAccountType(): string
{
$user = $this->getGameUser();
Expand Down
20 changes: 20 additions & 0 deletions src/Entity/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, Equatab
private ?int $id;
private string $username = '';
private string $email = '';
private mixed $avatar = '';
private bool $enabled = false;
private string $password = '';
private string $plainPassword = '';
Expand Down Expand Up @@ -176,6 +177,25 @@ public function setEmail(string $email): void
$this->email = $email;
}

public function getAvatar(): mixed
{
return $this->avatar;
}

public function setAvatar(mixed $avatar): void
{
$this->avatar = $avatar;
}

public function hasAvatar(): bool
{
$stats = fstat($this->avatar);
if ($stats !== false) {
return $stats['size'] > 0;
}
return false;
}

public function getLastLogin(): ?DateTime
{
return $this->lastLogin;
Expand Down
52 changes: 52 additions & 0 deletions src/Form/UploadAvatarType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace FrankProjects\UltimateWarfare\Form;

use FrankProjects\UltimateWarfare\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Image;

class UploadAvatarType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->setAction('/game/account/upload_avatar')
->add(
'avatar',
FileType::class,
[
'mapped' => false,
'label' => 'label.avatar',
'constraints' => [
new Image([
'maxSize' => '1024k',
])
],
]
)
->add(
'submit',
SubmitType::class,
[
'label' => 'label.upload'
]
);
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults(
array(
'data_class' => User::class,
'translation_domain' => 'account'
)
);
}
}
29 changes: 29 additions & 0 deletions src/Twig/Base64EncodeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace FrankProjects\UltimateWarfare\Twig;

use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;

class Base64EncodeExtension extends AbstractExtension
{
public const GET_CONTENT_FROM_STREAM = 'stream';

public function getFilters(): array
{
return [
new TwigFilter('base64_encode', [$this, 'base64Encode']),
];
}

public function base64Encode(mixed $string, string $modifier = ''): string
{
$content = match ($modifier) {
self::GET_CONTENT_FROM_STREAM => stream_get_contents($string),
default => (string) $string,
};
return base64_encode((string) $content);
}
}
12 changes: 12 additions & 0 deletions symfony.lock
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,18 @@
"templates/base.html.twig"
]
},
"symfony/validator": {
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.3",
"ref": "c32cfd98f714894c4f128bb99aa2530c1227603c"
},
"files": [
"config/packages/validator.yaml"
]
},
"symfony/var-dumper": {
"version": "v5.1.5"
},
Expand Down
Loading

0 comments on commit 5465bb6

Please sign in to comment.