} } return $response; } /** * Extend tree with additional statements and params * * @param array $addition * * @return array * * @since 6.5.3 https://github.com/aamplugin/advanced-access-manager/issues/122 * https://github.com/aamplugin/advanced-access-manager/issues/124 * @since 6.4.1 Simplified by removing &$tree first param * @since 6.4.0 Supporting Param's Value to be more than just a scalar value * @since 6.2.1 Typecasting param's value * @since 6.1.0 Added support for the `=>` (map to) operator * @since 6.0.0 Initial implementation of the method * * @access protected * @version 6.5.3 */ protected function updatePolicyTree($addition) { $stmts = &$this->tree['Statement']; $params = &$this->tree['Param']; $callback = array($this, 'getOption'); // Callback that hooks into get_option // Step #1. If there are any params, let's index them and insert into the list foreach ($addition['Param'] as $param) { if (!empty($param['Key'])) { $param['Value'] = $this->replaceTokens($param['Value'], true); foreach($this->evaluatePolicyKey($param['Key']) as $key) { if (!isset($params[$key]) || empty($params[$key]['Enforce'])) { $params[$key] = $param; // If "option:" - hooks to the WP core for override if (strpos($key, 'option:') === 0) { $name = substr($key, 7); // Hook into the core add_filter('pre_option_' . $name, $callback, 1, 2); add_filter('pre_site_option_' . $name, $callback, 1, 2); } } } } } // Step #2. If there are any statements, let's index them by resource:action // and insert into the list of statements foreach ($addition['Statement'] as $stm) { $resources = (isset($stm['Resource']) ? (array) $stm['Resource'] : array()); $actions = (isset($stm['Action']) ? (array) $stm['Action'] : array('')); foreach ($resources as $res) { foreach($this->evaluatePolicyKey($res) as $resource) { foreach ($actions as $act) { $id = strtolower($resource . (!empty($act) ? ":{$act}" : '')); if (isset($stmts[$id])) { if (isset($stmts[$id][0])) { $stmts[$id][] = $stm; } else { $stmts[$id] = array($stmts[$id], $stm); } } else { $stmts[$id] = $stm; } } } } } } /** * Evaluate resource name or param key * * The resource or param key may have tokens that build dynamic keys. This method * covers 3 possible scenario: * - Map To "=>" - the token should return array of values that are mapped to the * key; * - Token - returns scalar value; * - Raw Value - returns as-is * * @param string $key * * @return array * * @access protected * @version 6.4.1 */ protected function evaluatePolicyKey($key) { $response = array(); // Allow to build resource name or param key dynamically. if (preg_match('/^(.*)[\s]+(map to|=>)[\s]+(.*)$/i', $key, $match)) { // e.g. "Term:category:%s:posts => ${USER_META.regions}" // e.g. "%s:default:category => ${HTTP_POST.post_types}" $values = (array) AAM_Core_Policy_Token::getTokenValue($match[3]); // Create the map of resources/params and replace foreach($values as $value) { $response[] = sprintf($match[1], $value); } } elseif (preg_match_all('/(\$\{[^}]+\})/', $key, $match)) { // e.g. "Term:category:${USER_META.region}:posts" $response = array(AAM_Core_Policy_Token::evaluate($key, $match[1])); } else { $response = array($key); } return $response; } /** * Replace all the dynamic tokens recursively * * @param array $data * @param boolean $type_cast * * @return array * * @since 6.4.1 Added type casting param * @since 6.0.0 Initial implementation of the method * * @access protected * @version 6.4.1 */ protected function replaceTokens($data, $type_cast = false) { $replaced = array(); if (is_scalar($data)) { $replaced = $this->_replaceTokensInString($data, $type_cast); } else { foreach($data as $key => $value) { // Evaluate array's key and replace tokens $key = $this->_replaceTokensInString($key); // Evaluate array's value and replace tokens if (is_array($value)) { $replaced[$key] = $this->replaceTokens($value, $type_cast); } else { $replaced[$key] = $this->_replaceTokensInString( $value, $type_cast ); } } } return $replaced; } /** * Replace tokens is provided scalar string * * @param string $token * @param boolean $type_cast * * @return mixed * * @access private * @version 6.4.1 */ private function _replaceTokensInString($token, $type_cast = false) { if (preg_match_all('/(\$\{[^}]+\})/', $token, $match)) { $value = AAM_Core_Policy_Token::evaluate($token, $match[1]); if ($type_cast === true) { $replaced = AAM_Core_Policy_Typecast::execute($value); } else { $replaced = $value; } } else { $replaced = $token; } return $replaced; } /** * Perform some internal clean-up * * @param array &$statements * * @return void * * @since 6.5.3 https://github.com/aamplugin/advanced-access-manager/issues/124 * @since 6.0.0 Initial implementation of the method * * @access private * @version 6.5.3 */ private function _cleanupTree(&$statements) { foreach($statements as $id => &$stm) { if (is_array($stm) && isset($stm[0])) { $this->_cleanupTree($stm); } else { if (isset($stm['Resource'])) { unset($statements[$id]['Resource']); } if (isset($stm['Action'])) { unset($statements[$id]['Action']); } } } } /** * Check if policy block is applicable * * @param array $block * @param array $args * * @return boolean * * @access protected * @version 6.0.0 */ protected function isApplicable($block, $args = array()) { $result = true; if (!empty($block['Condition']) && is_array($block['Condition'])) { $result = AAM_Core_Policy_Condition::getInstance()->evaluate( $block['Condition'], $args ); } return $result; } }