<commit_before><?php

abstract class CM_Action_Abstract extends CM_Class_Abstract implements CM_ArrayConvertible, CM_Typed {

    const CREATE = 'CREATE';
    const UPDATE = 'UPDATE';
    const DELETE = 'DELETE';
    const ONLINE = 'ONLINE';
    const OFFLINE = 'OFFLINE';
    const VIEW = 'VIEW';
    const VISIBLE = 'VISIBLE';
    const INVISIBLE = 'INVISIBLE';
    const PUBLISH = 'PUBLISH';
    const UNPUBLISH = 'UNPUBLISH';
    const SUBSCRIBE = 'SUBSCRIBE';
    const UNSUBSCRIBE = 'UNSUBSCRIBE';

    /** @var CM_Model_User|int */
    protected $_actor = null;

    /** @var int */
    protected $_verb;

    /** @var int|null */
    protected $_ip = null;

    /** @var array */
    protected $_ignoreLogging = array();

    /** @var bool */
    private $_trackingEnabled = true;

    /** @var array */
    private $_trackingPropertyList = array();

    /**
     * @param string            $verbName
     * @param CM_Model_User|int $actor
     */
    public function __construct($verbName, $actor) {
        $this->setActor($actor);
        $this->_verb = CM_Action_Abstract::getVerbByVerbName($verbName);
    }

    protected function _notify() {
        $arguments = func_get_args();
        $methodName = '_notify' . CM_Util::camelize($this->getVerbName());

        if (method_exists($this, $methodName)) {
            call_user_func_array(array($this, $methodName), $arguments);
        }

        $this->_track();
    }

    /**
     * @return bool
     */
    protected function _isAllowed() {
        $arguments = func_get_args();
        $methodName = '_isAllowed' . CM_Util::camelize($this->getVerbName());

        if (method_exists($this, $methodName)) {
            return call_user_func_array(array($this, $methodName), $arguments);
        }
        return true;
    }

    /**
     * @return bool
     */
    public function isAllowed() {
        $arguments = func_get_args();
        if (!call_user_func_array(array($this, '_isAllowed'), $arguments)) {
            return false;
        }
        $actionLimitList = $this->getActionLimitsTransgressed();
        foreach ($actionLimitList as $actionLimitData) {
            /** @var CM_Model_ActionLimit_Abstract $actionLimit */
            $actionLimit = $actionLimitData['actionLimit'];
            if (!$actionLimit->getOvershootAllowed()) {
                return false;
            }
        }
        return true;
    }

    abstract protected function _prepare();

    public function prepare() {
        $arguments = func_get_args();
        if (!call_user_func_array(array($this, '_isAllowed'), $arguments)) {
            throw new CM_Exception_NotAllowed('Action not allowed', null, array('messagePublic' => 'The content you tried to interact with has become private.'));
        }
        $role = null;
        $actionLimitList = $this->getActionLimitsTransgressed();
        foreach ($actionLimitList as $actionLimitData) {
            /** @var CM_Model_ActionLimit_Abstract $actionLimit */
            $actionLimit = $actionLimitData['actionLimit'];
            $role = $actionLimitData['role'];
            $isFirst = $this->_isFirstActionLimit($actionLimit, $role);
            if ($isFirst) {
                $this->_log($actionLimit);
            }
            $actionLimit->overshoot($this, $role, $isFirst);
            if (!$actionLimit->getOvershootAllowed()) {
                throw new CM_Exception_NotAllowed('ActionLimit `' . $actionLimit->getType() . '` breached.');
            }
        }
        $this->_log();
        $this->_prepare();
    }

    /**
     * @return array ['actionLimit' => CM_Model_ActionLimit_Abstract, 'role' => int]
     */
    public function getActionLimitsTransgressed() {
        $actionLimitsTransgressed = array();
        /** @var CM_Model_ActionLimit_Abstract $actionLimit */
        foreach ($this->_getActionLimitList() as $actionLimit) {
            $bestRole = null;
            if ($this->getActor()) {
                $bestLimit = 0;
                foreach ($this->getActor()->getRoles()->get() as $role) {
                    $limit = $actionLimit->getLimit($role);
                    if ($limit === null || ($bestLimit !== null && $limit >= $bestLimit)) {
                        $bestRole = $role;
                        $bestLimit = $limit;
                    }
                }
            }
            $limit = $actionLimit->getLimit($bestRole);
            if ($limit !== null && ($limit == 0 || $limit <= $this->_getSiblings($actionLimit->getPeriod($bestRole))->getCount())) {
                $actionLimitsTransgressed[] = array('actionLimit' => $actionLimit, 'role' => $bestRole);
            }
        }
        return $actionLimitsTransgressed;
    }

    /**
     * @return CM_Model_User|null
     */
    public function getActor() {
        return $this->_actor;
    }

    /**
     * @param CM_Model_User|int $actor
     * @throws CM_Exception_Invalid
     */
    public function setActor($actor) {
        if ($actor instanceof CM_Model_User) {
            $this->_actor = $actor;
            $this->_ip = null;
        } elseif (is_int($actor) || ctype_digit($actor)) {
            $this->_actor = null;
            $this->_ip = $actor;
        } else {
            throw new CM_Exception_Invalid('Actor must be of type `CM_Model_User` or `int`');
        }
    }

    /**
     * @return int|null
     */
    public function getIp() {
        return $this->_ip;
    }

    /**
     * @return array
     */
    public function getTrackingPropertyList() {
        return $this->_trackingPropertyList;
    }

    /**
     * @return int
     */
    public function getVerb() {
        return $this->_verb;
    }

    /**
     * @return CM_Paging_ActionLimit_Action
     */
    protected function _getActionLimitList() {
        return new CM_Paging_ActionLimit_Action($this);
    }

    /**
     * @param CM_Model_ActionLimit_Abstract $actionLimit
     * @param int                           $role
     * @return bool
     */
    private function _isFirstActionLimit(CM_Model_ActionLimit_Abstract $actionLimit, $role) {
        $first = true;
        if (null !== $actionLimit->getLimit($role) && 0 !== $actionLimit->getLimit($role)) {
            $period = $actionLimit->getPeriod($role);
            $transgressions = $this->_getTransgressions($actionLimit->getType(), $period);
            if ($transgressions->getCount()) {
                $lastTransgression = $transgressions->getItem(0);
                $actions = $this->_getSiblings($period, $lastTransgression['createStamp']);
                if ($actions->getCount()) {
                    $firstAction = $actions->getItem(-1);
                    if (time() < ($firstAction['createStamp'] + $period)) {
                        $first = false;
                    }
                }
            }
        }
        return $first;
    }

    /**
     * @param int|null $within
     * @param int|null $upperBound
     * @return CM_Paging_Action_Ip|CM_Paging_Action_User
     * @throws CM_Exception_Invalid
     */
    private function _getSiblings($within = null, $upperBound = null) {
        if (in_array($this->getVerb(), $this->_ignoreLogging)) {
            throw new CM_Exception_Invalid(
                'Looking for actions of verb `' . $this->getVerb() . '` on actionType `' . $this->getType() . '` that is not being logged.');
        }
        if ($this->getActor()) {
            return $this->getActor()->getActions($this->getType(), $this->getVerb(), $within, $upperBound);
        } else {
            return new CM_Paging_Action_Ip($this->getIp(), $this->getType(), $this->getVerb(), $within, $upperBound);
        }
    }

    /**
     * @param int $actionLimitType OPTIONAL
     * @param int $period          OPTIONAL
     * @return CM_Paging_Transgression_Ip|CM_Paging_Transgression_User
     * @throws CM_Exception_Invalid
     */
    private function _getTransgressions($actionLimitType = null, $period = null) {
        if (in_array($this->getVerb(), $this->_ignoreLogging)) {
            throw new CM_Exception_Invalid(
                'Looking for transgressions of verb `' . $this->getVerb() . '` on actionType `' . $this->getType() . '` that is not being logged.');
        }
        if ($this->getActor()) {
            return $this->getActor()->getTransgressions($this->getType(), $this->getVerb(), $actionLimitType, $period);
        } else {
            return new CM_Paging_Transgression_Ip($this->getIp(), $this->getType(), $this->getVerb(), $actionLimitType, $period);
        }
    }

    /**
     * @param CM_Model_ActionLimit_Abstract|null $actionLimit
     */
    private function _log(CM_Model_ActionLimit_Abstract $actionLimit = null) {
        if (!in_array($this->getVerb(), $this->_ignoreLogging)) {
            if ($actionLimit) {
                $this->_getTransgressions()->add($this, $actionLimit->getType());
            } else {
                $this->_getSiblings()->add($this);
            }
        }
    }

    /**
     * @param int $age Seconds<commit_msg>Refactored $_ignoreLogging to $_actionLimitsEnabled in CM_Action_Abstract<commit_after><?php

abstract class CM_Action_Abstract extends CM_Class_Abstract implements CM_ArrayConvertible, CM_Typed {

    const CREATE = 'CREATE';
    const UPDATE = 'UPDATE';
    const DELETE = 'DELETE';
    const ONLINE = 'ONLINE';
    const OFFLINE = 'OFFLINE';
    const VIEW = 'VIEW';
    const VISIBLE = 'VISIBLE';
    const INVISIBLE = 'INVISIBLE';
    const PUBLISH = 'PUBLISH';
    const UNPUBLISH = 'UNPUBLISH';
    const SUBSCRIBE = 'SUBSCRIBE';
    const UNSUBSCRIBE = 'UNSUBSCRIBE';

    /** @var CM_Model_User|int */
    protected $_actor = null;

    /** @var int */
    protected $_verb;

    /** @var int|null */
    protected $_ip = null;

    /** @var bool */
    private $_actionLimitsEnabled = true;

    /** @var bool */
    private $_trackingEnabled = true;

    /** @var array */
    private $_trackingPropertyList = array();

    /**
     * @param string            $verbName
     * @param CM_Model_User|int $actor
     */
    public function __construct($verbName, $actor) {
        $this->setActor($actor);
        $this->_verb = CM_Action_Abstract::getVerbByVerbName($verbName);
    }

    protected function _notify() {
        $arguments = func_get_args();
        $methodName = '_notify' . CM_Util::camelize($this->getVerbName());

        if (method_exists($this, $methodName)) {
            call_user_func_array(array($this, $methodName), $arguments);
        }

        $this->_track();
    }

    /**
     * @return bool
     */
    protected function _isAllowed() {
        $arguments = func_get_args();
        $methodName = '_isAllowed' . CM_Util::camelize($this->getVerbName());

        if (method_exists($this, $methodName)) {
            return call_user_func_array(array($this, $methodName), $arguments);
        }
        return true;
    }

    /**
     * @return bool
     */
    public function isAllowed() {
        $arguments = func_get_args();
        if (!call_user_func_array(array($this, '_isAllowed'), $arguments)) {
            return false;
        }
        $actionLimitList = $this->getActionLimitsTransgressed();
        foreach ($actionLimitList as $actionLimitData) {
            /** @var CM_Model_ActionLimit_Abstract $actionLimit */
            $actionLimit = $actionLimitData['actionLimit'];
            if (!$actionLimit->getOvershootAllowed()) {
                return false;
            }
        }
        return true;
    }

    abstract protected function _prepare();

    public function prepare() {
        $arguments = func_get_args();
        if (!call_user_func_array(array($this, '_isAllowed'), $arguments)) {
            throw new CM_Exception_NotAllowed('Action not allowed', null, array('messagePublic' => 'The content you tried to interact with has become private.'));
        }
        $this->_checkActionLimits();
        $this->_prepare();
    }

    /**
     * @return array ['actionLimit' => CM_Model_ActionLimit_Abstract, 'role' => int]
     */
    public function getActionLimitsTransgressed() {
        $actionLimitsTransgressed = array();
        /** @var CM_Model_ActionLimit_Abstract $actionLimit */
        foreach ($this->_getActionLimitList() as $actionLimit) {
            $bestRole = null;
            if ($this->getActor()) {
                $bestLimit = 0;
                foreach ($this->getActor()->getRoles()->get() as $role) {
                    $limit = $actionLimit->getLimit($role);
                    if ($limit === null || ($bestLimit !== null && $limit >= $bestLimit)) {
                        $bestRole = $role;
                        $bestLimit = $limit;
                    }
                }
            }
            $limit = $actionLimit->getLimit($bestRole);
            if ($limit !== null && ($limit == 0 || $limit <= $this->_getSiblings($actionLimit->getPeriod($bestRole))->getCount())) {
                $actionLimitsTransgressed[] = array('actionLimit' => $actionLimit, 'role' => $bestRole);
            }
        }
        return $actionLimitsTransgressed;
    }

    /**
     * @return CM_Model_User|null
     */
    public function getActor() {
        return $this->_actor;
    }

    /**
     * @param CM_Model_User|int $actor
     * @throws CM_Exception_Invalid
     */
    public function setActor($actor) {
        if ($actor instanceof CM_Model_User) {
            $this->_actor = $actor;
            $this->_ip = null;
        } elseif (is_int($actor) || ctype_digit($actor)) {
            $this->_actor = null;
            $this->_ip = $actor;
        } else {
            throw new CM_Exception_Invalid('Actor must be of type `CM_Model_User` or `int`');
        }
    }

    /**
     * @return int|null
     */
    public function getIp() {
        return $this->_ip;
    }

    /**
     * @return array
     */
    public function getTrackingPropertyList() {
        return $this->_trackingPropertyList;
    }

    /**
     * @return int
     */
    public function getVerb() {
        return $this->_verb;
    }

    /**
     * @return CM_Paging_ActionLimit_Action
     */
    protected function _getActionLimitList() {
        return new CM_Paging_ActionLimit_Action($this);
    }

    private function _checkActionLimits() {
        if (!$this->_actionLimitsEnabled) {
            return;
        }
        $role = null;
        $actionLimitList = $this->getActionLimitsTransgressed();
        foreach ($actionLimitList as $actionLimitData) {
            /** @var CM_Model_ActionLimit_Abstract $actionLimit */
            $actionLimit = $actionLimitData['actionLimit'];
            $role = $actionLimitData['role'];
            $isFirst = $this->_isFirstActionLimit($actionLimit, $role);
            if ($isFirst) {
                $this->_log($actionLimit);
            }
            $actionLimit->overshoot($this, $role, $isFirst);
            if (!$actionLimit->getOvershootAllowed()) {
                throw new CM_Exception_NotAllowed('ActionLimit `' . $actionLimit->getType() . '` breached.');
            }
        }
        $this->_log();
    }

    /**
     * @param CM_Model_ActionLimit_Abstract $actionLimit
     * @param int                           $role
     * @return bool
     */
    private function _isFirstActionLimit(CM_Model_ActionLimit_Abstract $actionLimit, $role) {
        $first = true;
        if (null !== $actionLimit->getLimit($role) && 0 !== $actionLimit->getLimit($role)) {
            $period = $actionLimit->getPeriod($role);
            $transgressions = $this->_getTransgressions($actionLimit->getType(), $period);
            if ($transgressions->getCount()) {
                $lastTransgression = $transgressions->getItem(0);
                $actions = $this->_getSiblings($period, $lastTransgression['createStamp']);
                if ($actions->getCount()) {
                    $firstAction = $actions->getItem(-1);
                    if (time() < ($firstAction['createStamp'] + $period)) {
                        $first = false;
                    }
                }
            }
        }
        return $first;
    }

    /**
     * @param int|null $within
     * @param int|null $upperBound
     * @return CM_Paging_Action_Ip|CM_Paging_Action_User
     */
    private function _getSiblings($within = null, $upperBound = null) {
        if ($this->getActor()) {
            return $this->getActor()->getActions($this->getType(), $this->getVerb(), $within, $upperBound);
        } else {
            return new CM_Paging_Action_Ip($this->getIp(), $this->getType(), $this->getVerb(), $within, $upperBound);
        }
    }

    /**
     * @param int $actionLimitType OPTIONAL
     * @param int $period          OPTIONAL
     * @return CM_Paging_Transgression_Ip|CM_Paging_Transgression_User
     */
    private function _getTransgressions($actionLimitType = null, $period = null) {
        if ($this->getActor()) {
            return $this->getActor()->getTransgressions($this->getType(), $this->getVerb(), $actionLimitType, $period);
        } else {
            return new CM_Paging_Transgression_Ip($this->getIp(), $this->getType(), $this->getVerb(), $actionLimitType, $period);
        }
    }

    /**
     * @param CM_Model_ActionLimit_Abstract|null $actionLimit
     */
    private function _log(CM_Model_ActionLimit_Abstract $actionLimit = null) {
        if ($actionLimit) {
            $this->_getTransgressions()->add($this, $actionLimit->getType());
        } else {
            $this->_getSiblings()->add($this);
        }
    }

    /**
     * @param int $age Seconds
{'fraction_non_alphanumeric': 0.1400125163566024, 'fraction_numerical': 0.0005689253001080958, 'mean_word_length': 2.166636642046478, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 155, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3842678', 'n_tokens_mistral': 5119, 'n_tokens_neox': 5004, 'n_words': 984}