* @link https://www.tassos.gr * @copyright Copyright © 2024 Tassos All Rights Reserved * @license GNU GPLv3 or later */ namespace NRFramework\Conditions; defined('_JEXEC') or die; use Joomla\Registry\Registry; use Joomla\String\StringHelper; use Joomla\CMS\Language\Text; /** * Assignment Class */ class Condition { /** * Application Object * * @var object */ protected $app; /** * Document Object * * @var object */ protected $doc; /** * Date Object * * @var object */ protected $date; /** * Database Object * * @var object */ protected $db; /** * User Object * * @var object */ protected $user; /** * Assignment Selection * * @var mixed */ protected $selection; /** * Assignment Parameters * * @var mixed */ protected $params; /** * Assignment State (Include|Exclude) * * @var string */ public $assignment; /** * Options * * @var object */ public $options; /** * Framework factory object * * @var object */ public $factory; /** * The default operator that will be used to compare haystack with needle. * * @var string */ protected $operator; /** * Class constructor * * @param array $options The rule options. Expected properties: selection, value, params * @param object $factory The framework's factory class. */ public function __construct($options = null, $factory = null) { $this->factory = is_null($factory) ? new \NRFramework\Factory() : $factory; // Set General Joomla Objects $this->db = $this->factory->getDbo(); $this->app = $this->factory->getApplication(); $this->doc = $this->factory->getDocument(); $this->user = $this->factory->getUser(); $this->options = new Registry($options); $this->setParams($this->options->get('params')); $this->setOperator($this->options->get('operator', 'includesSome')); // For performance reasons we might move this inside the pass() method $this->setSelection($this->options->get('selection', '')); } /** * Set the rule's user selected value * * @param mixed $selection * @return object */ public function setSelection($selection) { $this->selection = $selection; if (method_exists($this, 'prepareSelection')) { $this->selection = $this->prepareSelection(); } return $this; } /** * Undocumented function * * @return void */ public function getSelection() { return $this->selection; } /** * Set the operator that will be used for the comparison * * @param string $operator * @return object */ public function setOperator($operator) { $this->operator = $operator; return $this; } /** * Set the rule's parameters * * @param array $params */ public function setParams($params) { $this->params = new Registry($params); } public function getParams() { return $this->params; } /** * Checks the validitity of two values based on the given operator. * * Consider converting this method as a Trait. * * @param mixed $value * @param mixed $selection * @param string $operator * @param array $options ignoreCase: true,false * * @return bool */ public function passByOperator($value = null, $selection = null, $operator = null, $options = null) { $value = is_null($value) ? $this->value() : $value; if (!is_null($selection)) { $this->setSelection($selection); } $selection = $this->getSelection(); $options = new Registry($options); $ignoreCase = $options->get('ignoreCase', true); if (is_object($value)) { $value = (array) $value; } if (is_object($selection)) { $selection = (array) $selection; } if ($ignoreCase) { if (is_string($value)) { $value = strtolower($value); } if (is_string($selection)) { $selection = strtolower($selection); } if (is_array($value)) { $value = array_map('strtolower', $value); } if (is_array($selection)) { $selection = array_map(function($str) { return is_null($str) ? '' : strtolower($str); }, $selection); } } $operator = (is_null($operator) OR empty($operator)) ? $this->operator : $operator; $pass = false; switch ($operator) { case 'exists': $pass = !is_null($value); break; // Determines whether haystack is empty. Accepts: array, string case 'empty': if (is_array($value)) { $pass = empty($value); } if (is_string($value)) { $pass = $value == '' || trim($value) == ''; } if (is_bool($value)) { $pass = !$value; } break; case 'equals': if (is_array($selection) || is_array($value)) { $pass = $this->passByOperator($value, $selection, 'includesSome', $options); } else { $pass = $value == $selection; } break; case 'contains': if (is_string($value) && is_string($selection)) { $pass = strlen($selection) > 0 && strpos($value, $selection) !== false; } break; // Determine whether haystack is less than needle. case 'less_than': case 'lowerthan': case 'lt': $pass = $value < $selection; break; // Determine whether haystack is less than or equal to needle. case 'less_than_or_equal_to': case 'lowerthanequal': case 'lte': $pass = $value <= $selection; break; // Determine whether haystack is greater than needle. case 'greater_than': case 'greaterthan': case 'gt': $pass = $value > $selection; break; // Determine whether haystack is greater than or equal to needle. case 'greater_than_or_equal_to': case 'greterthanequal': case 'gte': $pass = $value >= $selection; break; // Determine whether haystack contains all elements in needle. case 'includesAll': case 'containsall': $pass = count(array_intersect((array) $selection, (array) $value)) == count((array) $selection); break; // Determine whether haystack contains at least one element from needle. case 'includesSome': case 'containsany': $pass = !empty(array_intersect((array) $value, (array) $selection)); break; // Determine whether haystack contains at least one element from needle. Accepts; string, array. case 'includes': if (is_string($value) && $value != '' && is_string($selection) && $selection != '') { if (StringHelper::strpos($value, $selection) !== false) { $pass = true; } } if (is_array($value) || is_array($selection)) { $pass = $this->passByOperator($value, $selection, 'includesSome', $options); } break; // Determine whether haystack starts with needle. Accepts: string case 'starts_with': $pass = StringHelper::substr($value, 0, StringHelper::strlen($selection)) === $selection; break; // Determine whether haystack ends with needle. Accepts: string case 'ends_with': $pass = StringHelper::substr($value, -StringHelper::strlen($selection)) === $selection; break; // Determine whether value is in given range case 'range': $value1 = isset($selection['value1']) ? (float) $selection['value1'] : false; $value2 = isset($selection['value2']) ? (float) $selection['value2'] : false; $pass = $value1 && $value2 ? (($value >= $value1) && ($value <= $value2)) : false; break; // Determine whether haystack equals to needle. Accepts any object. default: $pass = $value == $selection; } return $pass; } /** * Base assignment check * * @return bool */ public function pass() { return $this->passByOperator(); } /** * Returns all parent rows * * This method doesn't belong here. Move it to Functions.php. * * @param integer $id Row primary key * @param string $table Table name * @param string $parent Parent column name * @param string $child Child column name * * @return array Array with IDs */ public function getParentIds($id = 0, $table = 'menu', $parent = 'parent_id', $child = 'id') { if (!$id) { return []; } $cache = $this->factory->getCache(); $hash = md5('getParentIds_' . $id . '_' . $table . '_' . $parent . '_' . $child); if ($cache->has($hash)) { return $cache->get($hash); } $parent_ids = array(); while ($id) { $query = $this->db->getQuery(true) ->select('t.' . $parent) ->from('#__' . $table . ' as t') ->where('t.' . $child . ' = ' . (int) $id); $this->db->setQuery($query); $id = $this->db->loadResult(); // Break if no parent is found or parent already found before for some reason if (!$id || in_array($id, $parent_ids)) { break; } $parent_ids[] = $id; } return $cache->set($hash, $parent_ids); } /** * A one-line text that describes the current value detected by the rule. Eg: The current time is %s. * * @return string */ public function getValueHint() { $value = $this->value(); // If the rule returns an array, use the 1st one. $value = is_array($value) ? $value[0] : $value; return Text::sprintf('NR_DISPLAY_CONDITIONS_HINT_' . strtoupper($this->getName()), ucfirst(strtolower($value))); } /** * Return the rule name * * @return string */ protected function getName() { $classParts = explode('\\', get_called_class()); return array_pop($classParts); } }