autowp/autowp

View on GitHub
module/Application/src/Controller/Api/UserController.php

Summary

Maintainability
D
2 days
Test Coverage
F
38%
<?php

namespace Application\Controller\Api;

use Application\Hydrator\Api\AbstractRestHydrator;
use Autowp\Image\Storage;
use Autowp\User\Controller\Plugin\User as UserPlugin;
use Autowp\User\Model\User;
use DateTimeZone;
use Exception;
use Imagick;
use Laminas\ApiTools\ApiProblem\ApiProblem;
use Laminas\ApiTools\ApiProblem\ApiProblemResponse;
use Laminas\Http\PhpEnvironment\Request;
use Laminas\Http\PhpEnvironment\Response;
use Laminas\InputFilter\InputFilter;
use Laminas\InputFilter\InputFilterInterface;
use Laminas\Mvc\Controller\AbstractRestfulController;
use Laminas\Stdlib\ResponseInterface;
use Laminas\Validator\InArray;
use Laminas\View\Model\JsonModel;
use Laminas\View\Model\ViewModel;

use function array_key_exists;
use function array_keys;
use function get_object_vars;
use function in_array;
use function is_array;
use function sprintf;

/**
 * @method UserPlugin user($user = null)
 * @method ApiProblemResponse inputFilterResponse(InputFilterInterface $inputFilter)
 * @method ViewModel forbiddenAction()
 * @method string language()
 * @method void log(string $message, array $objects)
 * @method string translate(string $message, string $textDomain = 'default', $locale = null)
 */
class UserController extends AbstractRestfulController
{
    private AbstractRestHydrator $hydrator;

    private InputFilter $itemInputFilter;

    private InputFilter $listInputFilter;

    private InputFilter $putInputFilter;

    private User $userModel;

    private InputFilter $postPhotoInputFilter;

    /** @var array<string, mixed> */
    private array $hosts;

    private Storage $imageStorage;

    public function __construct(
        AbstractRestHydrator $hydrator,
        InputFilter $itemInputFilter,
        InputFilter $listInputFilter,
        InputFilter $putInputFilter,
        InputFilter $postPhotoInputFilter,
        User $userModel,
        array $hosts,
        Storage $imageStorage
    ) {
        $this->hydrator             = $hydrator;
        $this->itemInputFilter      = $itemInputFilter;
        $this->listInputFilter      = $listInputFilter;
        $this->putInputFilter       = $putInputFilter;
        $this->postPhotoInputFilter = $postPhotoInputFilter;
        $this->userModel            = $userModel;
        $this->hosts                = $hosts;
        $this->imageStorage         = $imageStorage;
    }

    /**
     * @return ViewModel|ResponseInterface|array
     * @throws Exception
     */
    public function indexAction()
    {
        $user = $this->user()->get();

        $this->listInputFilter->setData($this->params()->fromQuery());

        if (! $this->listInputFilter->isValid()) {
            return $this->inputFilterResponse($this->listInputFilter);
        }

        $data = $this->listInputFilter->getValues();

        $filter = [
            'not_deleted' => true,
        ];

        $search = $data['search'];
        if ($search) {
            $filter['search'] = $search . '%';
        }

        if ($data['id']) {
            $filter['id'] = is_array($data['id']) ? $data['id'] : (int) $data['id'];
        }

        if ($data['identity']) {
            $filter['identity'] = $data['identity'];
        }

        $paginator = $this->userModel->getPaginator($filter);

        $limit = $data['limit'] ?: 1;

        $paginator
            ->setItemCountPerPage($limit)
            ->setCurrentPageNumber($data['page']);

        $this->hydrator->setOptions([
            'language' => $this->language(),
            'fields'   => $data['fields'],
            'user_id'  => $user ? $user['id'] : null,
        ]);

        $items = [];
        foreach ($paginator->getCurrentItems() as $row) {
            $items[] = $this->hydrator->extract($row);
        }

        return new JsonModel([
            'paginator' => get_object_vars($paginator->getPages()),
            'items'     => $items,
        ]);
    }

    /**
     * @return ViewModel|ResponseInterface|array
     * @throws Exception
     */
    public function itemAction()
    {
        $this->itemInputFilter->setData($this->params()->fromQuery());

        if (! $this->itemInputFilter->isValid()) {
            return $this->inputFilterResponse($this->itemInputFilter);
        }

        $data = $this->itemInputFilter->getValues();

        $user = $this->user()->get();

        $id = $this->params('id');

        if ($id === 'me') {
            if (! $user) {
                return new ApiProblemResponse(new ApiProblem(401, 'Not authorized'));
            }
            $id = $user['id'];
        }

        /** @psalm-suppress InvalidCast */
        $row = $this->userModel->getRow((int) $id);
        if (! $row) {
            return $this->notFoundAction();
        }

        $this->hydrator->setOptions([
            'language' => $this->language(),
            'fields'   => $data['fields'],
            'user_id'  => $user ? $user['id'] : null,
        ]);

        return new JsonModel($this->hydrator->extract($row));
    }

    /**
     * @return ViewModel|ResponseInterface|array
     * @throws Exception
     */
    public function putAction()
    {
        $user = $this->user()->get();

        if (! $user) {
            return new ApiProblemResponse(new ApiProblem(401, 'Not authorized'));
        }

        $id = $this->params('id');
        if ($id === 'me') {
            $id = $user['id'];
        }

        /** @psalm-suppress InvalidCast */
        $row = $this->userModel->getRow((int) $id);
        if (! $row) {
            return new ApiProblemResponse(new ApiProblem(404, 'Entity not found'));
        }

        $request = $this->getRequest();
        $data    = $this->processBodyContent($request);

        $fields = [];
        foreach (array_keys($data) as $key) {
            if ($this->putInputFilter->has($key)) {
                $fields[] = $key;
            }
        }

        if (! $fields) {
            return new ApiProblemResponse(new ApiProblem(400, 'No fields provided'));
        }

        if (in_array('language', $fields)) {
            // preload filter options
            $languages = [];
            foreach (array_keys($this->hosts) as $language) {
                $languages[] = $language;
            }
            $validators = $this->putInputFilter->get('language')->getValidatorChain()->getValidators();
            /** @var InArray $validator */
            $validator = $validators[0]['instance'];
            $validator->setHaystack($languages);
        }

        if (in_array('timezone', $fields)) {
            // preload filter options
            $list = [];
            foreach (DateTimeZone::listAbbreviations() as $group) {
                foreach ($group as $timeZone) {
                    $tzId = $timeZone['timezone_id'];
                    if ($tzId) {
                        $list[] = $tzId;
                    }
                }
            }

            $validators = $this->putInputFilter->get('timezone')->getValidatorChain()->getValidators();
            /** @var InArray $validator */
            $validator = $validators[0]['instance'];
            $validator->setHaystack($list);
        }

        $this->putInputFilter->setValidationGroup($fields);

        $this->putInputFilter->setData($data);
        if (! $this->putInputFilter->isValid()) {
            return $this->inputFilterResponse($this->putInputFilter);
        }

        $values = $this->putInputFilter->getValues();

        if (array_key_exists('language', $values)) {
            if ((int) $user['id'] !== (int) $row['id']) {
                return $this->forbiddenAction();
            }

            $this->userModel->getTable()->update([
                'language' => $values['language'],
            ], [
                'id' => $row['id'],
            ]);
        }

        if (array_key_exists('timezone', $values)) {
            if ((int) $user['id'] !== (int) $row['id']) {
                return $this->forbiddenAction();
            }

            $this->userModel->getTable()->update([
                'timezone' => $values['timezone'],
            ], [
                'id' => $row['id'],
            ]);
        }

        /** @var Response $response */
        $response = $this->getResponse();
        return $response->setStatusCode(Response::STATUS_CODE_200);
    }

    /**
     * @return ViewModel|ResponseInterface|array
     * @throws Exception
     */
    public function deletePhotoAction()
    {
        $user = $this->user()->get();

        $id = $this->params('id');
        if ($id === 'me') {
            if (! $user) {
                return new ApiProblemResponse(new ApiProblem(401, 'Not authorized'));
            }
            $id = $user['id'];
        }

        /** @psalm-suppress InvalidCast */
        $row = $this->userModel->getRow((int) $id);
        if (! $row) {
            return new ApiProblemResponse(new ApiProblem(404, 'Entity not found'));
        }

        $can = $this->user()->enforce('user', 'ban');
        if (! $can) {
            return $this->forbiddenAction();
        }

        $oldImageId = $row['img'];
        if ($oldImageId) {
            $this->userModel->getTable()->update([
                'img' => null,
            ], [
                'id' => $row['id'],
            ]);

            $this->imageStorage->removeImage($oldImageId);
        }

        $this->log(sprintf(
            'Удаление фотографии пользователя №%s',
            $row['id']
        ), [
            'users' => $row['id'],
        ]);

        /** @var Response $response */
        $response = $this->getResponse();
        return $response->setStatusCode(Response::STATUS_CODE_204);
    }

    /**
     * @return ViewModel|ResponseInterface|array
     * @throws Exception
     */
    public function postPhotoAction()
    {
        $user = $this->user()->get();

        if (! $user) {
            return $this->forbiddenAction();
        }

        $id = $this->params('id');
        if ($id === 'me') {
            $id = $user['id'];
        }

        /** @psalm-suppress InvalidCast */
        $row = $this->userModel->getRow((int) $id);
        if (! $row) {
            return $this->notFoundAction();
        }

        /** @var Request $request */
        $request = $this->getRequest();

        $data = $request->getFiles()->toArray();

        $this->postPhotoInputFilter->setData($data);
        if (! $this->postPhotoInputFilter->isValid()) {
            return $this->inputFilterResponse($this->postPhotoInputFilter);
        }

        $values = $this->postPhotoInputFilter->getValues();

        $imageSampler = $this->imageStorage->getImageSampler();

        $imagick = new Imagick();
        if (! $imagick->readImage($values['file']['tmp_name'])) {
            throw new Exception("Error loading image");
        }
        $format = $this->imageStorage->getFormat('photo');
        $imageSampler->convertImagick($imagick, null, $format);

        $newImageId = $this->imageStorage->addImageFromImagick($imagick, 'user', [
            's3' => true,
        ]);

        $imagick->clear();

        $oldImageId = $row['img'];

        $this->userModel->getTable()->update([
            'img' => $newImageId,
        ], [
            'id' => $row['id'],
        ]);

        if ($oldImageId) {
            $this->imageStorage->removeImage($oldImageId);
        }

        /** @var Response $response */
        $response = $this->getResponse();
        return $response->setStatusCode(Response::STATUS_CODE_201);
    }
}