autowp/autowp

View on GitHub
module/Application/src/Hydrator/Api/UserHydrator.php

Summary

Maintainability
D
1 day
Test Coverage
C
76%
<?php

namespace Application\Hydrator\Api;

use Application\Model\Picture;
use Application\Model\UserAccount;
use ArrayAccess;
use Autowp\Commons\Db\Table\Row;
use Autowp\User\Model\User;
use Casbin\Enforcer;
use Casbin\Exceptions\CasbinException;
use DateInterval;
use DateTime;
use Exception;
use Laminas\Hydrator\Exception\InvalidArgumentException;
use Laminas\Hydrator\Strategy\DateTimeFormatterStrategy;
use Laminas\ServiceManager\ServiceLocatorInterface;
use Laminas\Stdlib\ArrayUtils;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Traversable;

use function hash;
use function inet_ntop;
use function is_array;
use function md5;
use function sprintf;
use function strtolower;
use function trim;
use function urlencode;

class UserHydrator extends AbstractRestHydrator
{
    protected int $userId = 0;

    private ?string $userRole;

    private Enforcer $acl;

    private User $userModel;

    private UserAccount $userAccount;

    private Picture $picture;

    /**
     * @throws NotFoundExceptionInterface
     * @throws ContainerExceptionInterface
     */
    public function __construct(ServiceLocatorInterface $serviceManager)
    {
        parent::__construct();

        $this->acl         = $serviceManager->get(Enforcer::class);
        $this->userModel   = $serviceManager->get(User::class);
        $this->userAccount = $serviceManager->get(UserAccount::class);
        $this->picture     = $serviceManager->get(Picture::class);

        $strategy = new DateTimeFormatterStrategy();
        $this->addStrategy('last_online', $strategy);
        $this->addStrategy('reg_date', $strategy);
        $this->addStrategy('rename_date', $strategy);

        $strategy = new Strategy\Image($serviceManager); // @phpstan-ignore-line
        $this->addStrategy('image', $strategy);

        $strategy = new Strategy\Image($serviceManager); // @phpstan-ignore-line
        $this->addStrategy('img', $strategy);

        $strategy = new Strategy\Image($serviceManager); // @phpstan-ignore-line
        $this->addStrategy('avatar', $strategy);
    }

    /**
     * @param  array|Traversable $options
     * @throws InvalidArgumentException
     */
    public function setOptions($options): self
    {
        parent::setOptions($options);

        if ($options instanceof Traversable) {
            $options = ArrayUtils::iteratorToArray($options);
        } elseif (! is_array($options)) {
            throw new InvalidArgumentException(
                'The options parameter must be an array or a Traversable'
            );
        }

        if (isset($options['user_id'])) {
            $this->setUserId($options['user_id']);
        }

        return $this;
    }

    /**
     * @param array|ArrayAccess $object
     * @throws Exception
     */
    public function extract($object): ?array
    {
        $deleted = (bool) $object['deleted'];

        $isMe = (int) $object['id'] === $this->userId;

        if ($deleted) {
            $user = [
                'id'       => null,
                'name'     => null,
                'deleted'  => true,
                'url'      => null,
                'longAway' => false,
                'green'    => false,
            ];
        } else {
            $longAway   = false;
            $lastOnline = Row::getDateTimeByColumnType('timestamp', $object['last_online']);
            if ($lastOnline) {
                $date = new DateTime();
                $date->sub(new DateInterval('P6M'));
                if ($date > $lastOnline) {
                    $longAway = true;
                }
            } else {
                $longAway = true;
            }

            $isGreen = $object['role'] && $this->acl->enforce($object['role'], 'status', 'be-green');

            $user = [
                'id'           => (int) $object['id'],
                'name'         => $object['name'],
                'deleted'      => false,
                'route'        => ['/users', $object['identity'] ? $object['identity'] : 'user' . $object['id']],
                'long_away'    => $longAway,
                'green'        => $isGreen,
                'identity'     => $object['identity'],
                'specs_weight' => (float) $object['specs_weight'],
            ];

            if ($this->filterComposite->filter('last_online')) {
                $lastOnline          = Row::getDateTimeByColumnType('timestamp', $object['last_online']);
                $user['last_online'] = $this->extractValue('last_online', $lastOnline);
            }

            if ($this->filterComposite->filter('reg_date')) {
                $regDate          = Row::getDateTimeByColumnType('timestamp', $object['reg_date']);
                $user['reg_date'] = $this->extractValue('reg_date', $regDate);
            }

            if ($this->filterComposite->filter('image')) {
                $user['image'] = $this->extractValue('image', [
                    'image' => $object['img'],
                ]);
            }

            $canViewEmail = $isMe;
            if (! $canViewEmail) {
                $canViewEmail = $this->isModer();
            }

            if ($canViewEmail && $this->filterComposite->filter('email')) {
                $user['email'] = $object['e_mail'];
            }

            $canViewLogin = $isMe;
            if (! $canViewLogin) {
                $canViewLogin = $this->isModer();
            }

            if ($canViewLogin && $this->filterComposite->filter('login')) {
                $user['login'] = $object['login'];
            }

            if ($this->filterComposite->filter('img')) {
                $user['img'] = $this->extractValue('img', [
                    'image' => $object['img'],
                ]);
            }

            if ($this->filterComposite->filter('avatar')) {
                $user['avatar'] = $this->extractValue('image', [
                    'image'  => $object['img'],
                    'format' => 'avatar',
                ]);
            }

            if ($this->filterComposite->filter('photo')) {
                $user['photo'] = $this->extractValue('image', [
                    'image'  => $object['img'],
                    'format' => 'photo',
                ]);
            }

            if ($this->filterComposite->filter('gravatar') && $object['e_mail']) {
                $user['gravatar'] = sprintf(
                    'https://www.gravatar.com/avatar/%s?s=70&d=%s&r=g',
                    hash('sha256', strtolower(trim($object['e_mail']))),
                    urlencode('https://www.autowp.ru/_.gif')
                );
            }

            if ($this->filterComposite->filter('gravatar_hash') && $object['e_mail']) {
                $user['gravatar_hash'] = md5($object['e_mail']);
            }

            if ($isMe && $this->filterComposite->filter('language')) {
                $user['language'] = $object['language'];
            }

            if ($isMe && $this->filterComposite->filter('timezone')) {
                $user['timezone'] = $object['timezone'];
            }

            if ($isMe && $this->filterComposite->filter('votes_left')) {
                $user['votes_left'] = (int) $object['votes_left'];
            }

            if ($isMe && $this->filterComposite->filter('votes_per_day')) {
                $user['votes_per_day'] = (int) $object['votes_per_day'];
            }

            if ($this->filterComposite->filter('is_moder')) {
                $user['is_moder'] = $this->acl->enforce($object['role'], 'global', 'moderate');
            }

            if ($this->filterComposite->filter('accounts')) {
                $user['accounts'] = $this->userAccount->getAccounts($object['id']);
            }

            if ($this->filterComposite->filter('pictures_added')) {
                $user['pictures_added'] = (int) $object['pictures_added'];
            }

            if ($this->filterComposite->filter('pictures_accepted_count')) {
                $user['pictures_accepted_count'] = $this->picture->getCount([
                    'user'   => $user['id'],
                    'status' => Picture::STATUS_ACCEPTED,
                ]);
            }

            if ($this->filterComposite->filter('last_ip')) {
                $canViewIp = false;
                $role      = $this->getUserRole();
                if ($role) {
                    $canViewIp = $this->acl->enforce($role, 'user', 'ip');
                }

                if ($canViewIp) {
                    $user['last_ip'] = inet_ntop($object['last_ip']);
                }
            }
        }

        return $user;
    }

    /**
     * @throws CasbinException
     * @throws Exception
     */
    private function isModer(): bool
    {
        $role = $this->getUserRole();
        if (! $role) {
            return false;
        }

        return $this->acl->enforce($role, 'global', 'moderate');
    }

    /**
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     * @param object $object
     * @throws Exception
     */
    public function hydrate(array $data, $object): object
    {
        throw new Exception("Not supported");
    }

    public function setUserId(int $userId): self
    {
        if ($this->userId !== $userId) {
            $this->userId   = $userId;
            $this->userRole = null;
        }

        return $this;
    }

    /**
     * @throws Exception
     */
    private function getUserRole(): ?string
    {
        if (! $this->userId) {
            return null;
        }

        if (! isset($this->userRole)) {
            $this->userRole = $this->userModel->getUserRole($this->userId);
        }

        return $this->userRole;
    }
}