Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/Service/User/TempChange.php
<?php

namespace XF\Service\User;

use function
count, in_array;

class
TempChange extends \XF\Service\AbstractService
{
    protected
$validChangeRelations = ['Auth', 'Option', 'Profile', 'Privacy'];

    public function
applyFieldChange(\XF\Entity\User $user, $changeKey, $field, $newValue, $expiryDate = null)
    {
        return
$this->applyChange(
           
$user, $changeKey, $expiryDate, function(\XF\Entity\User $user) use ($field, $newValue)
            {
               
$changeEntField = $this->identifyFieldChangeEntity($user, $field);
                if (!
$changeEntField)
                {
                    throw new \
InvalidArgumentException("Cannot change $field");
                }

               
/** @var \XF\Mvc\Entity\Entity $changeEnt */
               
list($changeEnt, $finalField) = $changeEntField;

               
$oldestChange = $this->finder('XF:UserChangeTemp')->where([
                   
'user_id' => $user->user_id,
                   
'action_type' => 'field',
                   
'action_modifier' => $field
               
])->order('create_date')->fetchOne();
               
$oldValue = $oldestChange ? $oldestChange->old_value : $changeEnt->getValueSourceEncoded($finalField);

               
$changeEnt->set($finalField, $newValue);
                if (
$changeEnt->isChanged($finalField))
                {
                   
$changeEnt->save(false, false);
                }

               
$newValue = $changeEnt->getValueSourceEncoded($finalField);

                return [
                   
'action_type' => 'field',
                   
'action_modifier' => $field,
                   
'new_value' => $newValue,
                   
'old_value' => $oldValue
               
];
            }
        );
    }

    public function
applyGroupChange(\XF\Entity\User $user, $changeKey, array $addGroups, $groupChangeKey = null, $expiryDate = null)
    {
        return
$this->applyChange(
           
$user, $changeKey, $expiryDate, function(\XF\Entity\User $user) use ($groupChangeKey, $addGroups)
            {
                if (!
$groupChangeKey)
                {
                   
$groupChangeKey = 'user_change_' . substr(md5(uniqid()), 0, 16);
                }

               
/** @var \XF\Service\User\UserGroupChange $changeService */
               
$changeService = $this->service('XF:User\UserGroupChange');
               
$changeService->addUserGroupChange($user->user_id, $groupChangeKey, $addGroups);

                return [
                   
'action_type' => 'groups',
                   
'action_modifier' => $groupChangeKey
               
];
            }
        );
    }

    protected function
applyChange(\XF\Entity\User $user, $changeKey, $expiryDate, \Closure $applier)
    {
       
$this->db()->beginTransaction();

        if (
$changeKey !== null)
        {
           
$this->expireUserChangeByKey($user, $changeKey);
        }

        try
        {
           
$applyResult = $applier($user);
        }
        catch (\
Exception $e)
        {
           
$this->db()->rollback();
            throw
$e;
        }

       
$values = array_merge([
           
'user_id' => $user->user_id,
           
'change_key' => $changeKey,
           
'new_value' => null,
           
'old_value' => null,
           
'expiry_date' => $expiryDate
       
], $applyResult);

       
$change = $this->em()->instantiateEntity('XF:UserChangeTemp');
       
$change->bulkSet($values);
       
$change->save(true, false);

       
$this->db()->commit();

        return
$change;
    }

    public function
expireUserChangeByKey(\XF\Entity\User $user, $changeKey)
    {
       
/** @var \XF\Entity\UserChangeTemp|null $change */
       
$change = $this->em()->findOne('XF:UserChangeTemp', ['user_id' => $user->user_id, 'change_key' => $changeKey]);
        if (
$change)
        {
            return
$this->expireChange($change);
        }

        return
false;
    }

    public function
expireChange(\XF\Entity\UserChangeTemp $change)
    {
       
/** @var \XF\Entity\User $user */
       
$user = $change->User;

       
$this->em()->detachEntity($change);
       
$success = $this->db()->delete('xf_user_change_temp', 'user_change_temp_id = ? ', $change->user_change_temp_id);
        if (!
$success || !$user)
        {
            return
false;
        }

       
$this->db()->beginTransaction();

        switch (
$change->action_type)
        {
            case
'groups':
               
$groupChangeKey = $change->action_modifier;

               
/** @var \XF\Service\User\UserGroupChange $changeService */
               
$changeService = $this->service('XF:User\UserGroupChange');
               
$changeService->removeUserGroupChange($user->user_id, $groupChangeKey);
                break;

            case
'field':
               
$field = $change->action_modifier;

               
$changeEntField = $this->identifyFieldChangeEntity($user, $field);
                if (!
$changeEntField)
                {
                   
// We successfully inserted this, but it's not working now. This is probably related to
                    // a field from an add-on. We need to just ignore it and carry on.
                   
break;
                }

               
/** @var \XF\Mvc\Entity\Entity $changeEnt */
               
list($changeEnt, $finalField) = $changeEntField;

                if ((string)
$change->new_value !== (string)$changeEnt->getValueSourceEncoded($finalField))
                {
                   
// the field value has been changed from our programmatic change, can't do anything.
                    // note we need to force string comparison as new_value is a blob field; might be comparing to int field
                   
break;
                }

               
// we're either going to revert to the latest remaining programmatic change or back to what we
                // were at if that doesn't exist
               
$newestChange = $this->finder('XF:UserChangeTemp')->where([
                   
'user_id' => $user->user_id,
                   
'action_type' => 'field',
                   
'action_modifier' => $field
               
])->order('create_date', 'desc')->fetchOne();
               
$revertValue = $newestChange ? $newestChange->new_value : $change->old_value;
               
$changeEnt->setFromEncoded($finalField, $revertValue);
                if (
$changeEnt->isChanged($finalField))
                {
                   
$changeEnt->save(false, false);
                }
                break;
        }

       
$this->db()->commit();

        return
true;
    }

    protected function
identifyFieldChangeEntity(\XF\Entity\User $user, $changeField)
    {
       
$parts = explode('.', $changeField);
        if (
count($parts) == 1)
        {
            if (
$user->isValidColumn($changeField))
            {
                return [
$user, $changeField];
            }

            foreach (
$this->validChangeRelations AS $relation)
            {
               
$key = "{$relation}.{$changeField}";
               
$valid = $this->identifyFieldChangeEntity($user, $key);
                if (
$valid)
                {
                    return
$valid;
                }
            }

            return
null;
        }
        else if (
count($parts) == 2)
        {
            list(
$relation, $field) = $parts;
            if (!
in_array($relation, $this->validChangeRelations))
            {
                throw new \
InvalidArgumentException("Relation $relation is not a valid user change relation");
            }

           
/** @var \XF\Mvc\Entity\Entity $subEnt */
           
$subEnt = $user->{$relation};
            if (
$subEnt && $subEnt->isValidColumn($field))
            {
                return [
$subEnt, $field];
            }

            return
null;
        }
        else
        {
            throw new \
InvalidArgumentException("Change field $changeField is not valid, may only reference one relation");
        }
    }
}