393 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			393 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| /**
 | |
|  *  @author          Tassos.gr <info@tassos.gr>
 | |
|  *  @link            https://www.tassos.gr
 | |
|  *  @copyright       Copyright © 2024 Tassos All Rights Reserved
 | |
|  *  @license         GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
 | |
| */
 | |
| 
 | |
| namespace NRFramework\Conditions;
 | |
| 
 | |
| defined('_JEXEC') or die;
 | |
| 
 | |
| use Joomla\CMS\Layout\LayoutHelper;
 | |
| use NRFramework\Conditions\ConditionsHelper;
 | |
| use NRFramework\Extension;
 | |
| use Joomla\CMS\Language\Text;
 | |
| use Joomla\CMS\Uri\Uri;
 | |
| use Joomla\CMS\HTML\HTMLHelper;
 | |
| use Joomla\CMS\Form\Form;
 | |
| 
 | |
| class ConditionBuilder
 | |
| {
 | |
|     public static function pass($rules)
 | |
|     {
 | |
|         $rules = self::prepareRules($rules);
 | |
| 
 | |
|         if (empty($rules))
 | |
|         {
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         return ConditionsHelper::getInstance()->passSets($rules);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Prepare rules object to run checks
 | |
|      *
 | |
|      * @return void
 | |
|      */
 | |
|     public static function prepareRules($rules = [])
 | |
|     {
 | |
|         if (!is_array($rules))
 | |
|         {
 | |
|             return [];
 | |
|         }
 | |
|         
 | |
|         $rules_ = [];
 | |
| 
 | |
|         foreach ($rules as $key => $group)
 | |
|         {
 | |
|             if (isset($group['enabled']) AND !(bool) $group['enabled'])
 | |
|             {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             // A group without rules, doesn't make sense.
 | |
|             if (!isset($group['rules']) OR (isset($group['rules']) AND empty($group['rules'])))
 | |
|             {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             $validRules = [];
 | |
| 
 | |
|             foreach ($group['rules'] as $rule)
 | |
|             {
 | |
|                 // Make sure rule has a name.
 | |
|                 if (!isset($rule['name']) OR (isset($rule['name']) AND empty($rule['name'])))
 | |
|                 {
 | |
|                     continue;
 | |
|                 }
 | |
| 
 | |
|                 // Rule is invalid if both value and params properties are empty
 | |
|                 if (!isset($rule['value']) && !isset($rule['params']))
 | |
|                 {
 | |
|                     continue;
 | |
|                 }
 | |
| 
 | |
|                 // Skip disabled rules
 | |
|                 if (isset($rule['enabled']) && !(bool) $rule['enabled'])
 | |
|                 {
 | |
|                     continue;
 | |
|                 }
 | |
| 
 | |
|                 // We don't need this property.
 | |
|                 unset($rule['enabled']);
 | |
| 
 | |
|                 // Prepare rule value if necessary
 | |
|                 if (isset($rule['value']))
 | |
|                 {
 | |
|                     $rule['value'] = self::prepareTFRepeaterValue($rule['value']);
 | |
|                 }
 | |
| 
 | |
|                 // Verify operator
 | |
|                 if (!isset($rule['operator']) OR (isset($rule['operator']) && empty($rule['operator'])))
 | |
|                 {
 | |
|                     $rule['operator'] = isset($rule['params']['operator']) ? $rule['params']['operator'] : '';
 | |
|                 }
 | |
| 
 | |
|                 $validRules[] = $rule;
 | |
|             }
 | |
| 
 | |
|             if (count($validRules) > 0)
 | |
|             {
 | |
|                 $group['rules'] = $validRules;
 | |
| 
 | |
|                 if (!isset($group['matching_method']) OR (isset($group['matching_method']) AND empty($group['matching_method'])))
 | |
|                 {
 | |
|                     $group['matching_method'] = 'all';
 | |
|                 }
 | |
| 
 | |
|                 unset($group['enabled']);
 | |
|                 $rules_[] = $group;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return $rules_;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Parse the value of the TF Repeater Input field.
 | |
|      * 
 | |
|      * @param   array  $selection
 | |
|      * 
 | |
|      * @return  mixed
 | |
|      */
 | |
|     public static function prepareTFRepeaterValue($selection)
 | |
|     {
 | |
|         // Only proceed when we have an array of arrays selection.
 | |
|         if (!is_array($selection))
 | |
|         {
 | |
|             return $selection;
 | |
|         }
 | |
| 
 | |
|         $first = array_values($selection)[0];
 | |
| 
 | |
|         if (!is_array($first))
 | |
|         {
 | |
|             return $selection;
 | |
|         }
 | |
| 
 | |
|         if (!isset($first['value']))
 | |
|         {
 | |
|             return $selection;
 | |
|         }
 | |
| 
 | |
|         $new_selection = [];
 | |
| 
 | |
|         foreach ($selection as $value)
 | |
|         {
 | |
|             /**
 | |
|             * We expect a `value` key for TFInputRepeater fields or a key,value pair
 | |
|             * for plain arrays.
 | |
|             */
 | |
|             if (!isset($value['value']))
 | |
|             {
 | |
|                 /**
 | |
|                 * If no value exists, it means that the passed $assignment->selection is a key,value pair array so we use the value
 | |
|                 * as our returned selection.
 | |
|                 * 
 | |
|                 * This happens when we pass a key,value pair array as $assignment->selection when we expect a TFInputRepeater value
 | |
|                 * so we need to take this into consideration.
 | |
|                 */
 | |
|                 $new_selection[] = $value;
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             // value must not be empty
 | |
|             if (is_scalar($value['value']) && empty(trim($value['value'])))
 | |
|             {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             $new_selection[] = count($value) === 1 ? $value['value'] : $value;
 | |
|         }
 | |
| 
 | |
|         return $new_selection;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the TGeoIP plugin modal.
 | |
|      * 
 | |
|      * @return  string
 | |
|      */
 | |
|     public static function getGeoModal()
 | |
|     {
 | |
|         // Do not proceed if the database is up-to-date
 | |
|         if (!\NRFramework\Extension::geoPluginNeedsUpdate())
 | |
|         {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         HTMLHelper::_('bootstrap.modal');
 | |
| 
 | |
|         $modalName = 'tf-geodbchecker-modal';
 | |
| 
 | |
|         // The TGeoIP Plugin URL
 | |
|         $url = Uri::base(true) . '/index.php?option=com_plugins&view=plugin&tmpl=component&layout=modal&extension_id=' . \NRFramework\Functions::getExtensionID('tgeoip', 'system');
 | |
| 
 | |
|         $options = [
 | |
|             'title'       => Text::_('NR_EDIT'),
 | |
|             'url'         => $url,
 | |
|             'height'      => '400px',
 | |
|             'backdrop'    => 'static',
 | |
|             'bodyHeight'  => '70',
 | |
|             'modalWidth'  => '70',
 | |
|             'footer'      => '<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" data-dismiss="modal" aria-hidden="true">'
 | |
|                     . Text::_('JLIB_HTML_BEHAVIOR_CLOSE') . '</button>                 
 | |
|                     <button type="button" class="btn btn-success" aria-hidden="true"
 | |
|                     onclick="jQuery(\'#' . $modalName . ' iframe\').contents().find(\'#applyBtn\').click();">'
 | |
|                     . Text::_('JAPPLY') . '</button>',
 | |
|         ];
 | |
| 
 | |
|         return HTMLHelper::_('bootstrap.renderModal', $modalName, $options);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Prepares the given rules list.
 | |
|      * 
 | |
|      * @param   array  $list
 | |
|      * 
 | |
|      * @return  array
 | |
|      */
 | |
|     public static function prepareXmlRulesList($list)
 | |
|     {
 | |
|         if (is_array($list))
 | |
|         {
 | |
|             $list = implode(',', array_map('trim', $list));
 | |
|         }
 | |
|         else if (is_string($list))
 | |
|         {
 | |
|             $list = str_replace(' ', '', $list);
 | |
|         }
 | |
| 
 | |
|         return $list;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Adds a new condition item or group.
 | |
|      * 
 | |
|      * @param   string  $controlGroup       The name of the input used to store the data.
 | |
|      * @param   string  $groupKey           The group index ID.
 | |
|      * @param   string  $conditionKey       The added condition item index ID.
 | |
|      * @param   array   $condition          The condition name we are adding.
 | |
|      * @param   string  $include_rules      The list of included conditions that override the available conditions.
 | |
|      * @param   string  $exclude_rules      The list of excluded conditions that override the available conditions.
 | |
|      * @param   bool    $exclude_rules_pro  Whether the excluded rules should appear as Pro missing features.
 | |
|      * 
 | |
|      * @return  string
 | |
|      */
 | |
|     public static function add($controlGroup, $groupKey, $conditionKey, $condition = null, $include_rules = [], $exclude_rules = [], $exclude_rules_pro = false)
 | |
|     {
 | |
|         $controlGroup_ = $controlGroup . "[$groupKey][rules][$conditionKey]"; // @Todo - rename input namespace to 'conditions'
 | |
|         $form = self::getForm('conditionbuilder/base.xml', $controlGroup_, $condition);
 | |
|         $form->setFieldAttribute('name', 'include_rules', is_array($include_rules) ? implode(',', $include_rules) : $include_rules);
 | |
|         $form->setFieldAttribute('name', 'exclude_rules', is_array($exclude_rules) ? implode(',', $exclude_rules) : $exclude_rules);
 | |
|         $form->setFieldAttribute('name', 'exclude_rules_pro', $exclude_rules_pro);
 | |
| 
 | |
|         $options = [
 | |
|             'name'              => $controlGroup_,
 | |
|             'enabled'           => !isset($condition['enabled']) ? true : (string) $condition['enabled'] == '1',
 | |
|             'toolbar'           => $form,
 | |
|             'groupKey'          => $groupKey,
 | |
|             'conditionKey'      => $conditionKey,
 | |
|             'options'           => ''
 | |
|         ];
 | |
| 
 | |
|         if (isset($condition['name']))
 | |
|         {
 | |
|             $optionsHTML = self::renderOptions($condition['name'], $controlGroup_, $condition);
 | |
|             $options['condition_name'] = $condition['name'];
 | |
|             $options['options'] = $optionsHTML;
 | |
|         }
 | |
| 
 | |
|         return self::getLayout('conditionbuilder_row', $options);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Render condition item settings.
 | |
|      * 
 | |
|      * @param   string  $name          The name of the condition item.
 | |
|      * @param   string  $controlGroup  The name of the input used to store the data.
 | |
|      * @param   object  $formData      The data that will be bound to the form.
 | |
|      * 
 | |
|      * @return  string
 | |
|      */
 | |
|     public static function renderOptions($name, $controlGroup = null, $formData = null)
 | |
|     {
 | |
|         if (!$form = self::getForm('conditions/' . strtolower(str_replace('\\', '/', $name)) . '.xml', $controlGroup, $formData))
 | |
|         {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         $form->setFieldAttribute('note', 'ruleName', $name);
 | |
| 
 | |
|         return $form->renderFieldset('general');
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Handles loading condition builder given a payload.
 | |
|      * 
 | |
|      * @param   array   $payload
 | |
|      * 
 | |
|      * @return  string
 | |
|      */
 | |
|     public static function initLoad($payload = [])
 | |
|     {
 | |
|         if (!$payload)
 | |
|         {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (!isset($payload['data']) &&
 | |
|             !isset($payload['name']))
 | |
|         {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (!$data = json_decode($payload['data']))
 | |
|         {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         // transform object to assosiative array
 | |
|         $data = json_decode(json_encode($data), true);
 | |
| 
 | |
|         // html of condition builder
 | |
|         $html = '';
 | |
| 
 | |
|         $include_rules = isset($payload['include_rules']) ? $payload['include_rules'] : [];
 | |
|         $exclude_rules = isset($payload['exclude_rules']) ? $payload['exclude_rules'] : [];
 | |
|         $exclude_rules_pro = isset($payload['exclude_rules_pro']) ? $payload['exclude_rules_pro'] : false;
 | |
| 
 | |
|         foreach ($data as $groupKey => $groupConditions)
 | |
|         {
 | |
|             $payload = [
 | |
|                 'name' => $payload['name'],
 | |
|                 'groupKey' => $groupKey,
 | |
|                 'groupConditions' => $groupConditions,
 | |
|                 'include_rules' => $include_rules,
 | |
|                 'exclude_rules' => $exclude_rules,
 | |
|                 'exclude_rules_pro' => $exclude_rules_pro
 | |
|             ];
 | |
|             
 | |
|             $html .= self::getLayout('conditionbuilder_group', $payload);
 | |
|             
 | |
|         }
 | |
| 
 | |
|         return $html;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Render a layout given its name and payload.
 | |
|      * 
 | |
|      * @param   string  $name
 | |
|      * @param   array   $payload
 | |
|      * 
 | |
|      * @return  string
 | |
|      */
 | |
|     public static function getLayout($name, $payload)
 | |
|     {
 | |
|         return LayoutHelper::render($name, $payload, JPATH_PLUGINS . '/system/nrframework/layouts');
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the form by binding given data.
 | |
|      * 
 | |
|      * @param   string  $name
 | |
|      * @param   string  $controlGroup
 | |
|      * @param   array   $data
 | |
|      * 
 | |
|      * @return  object
 | |
|      */
 | |
|     private static function getForm($name, $controlGroup, $data = null)
 | |
|     {
 | |
|         if (!file_exists(JPATH_PLUGINS . '/system/nrframework/xml/' . $name))
 | |
|         {
 | |
|             return;
 | |
|         }
 | |
|         
 | |
|         $form = new Form('cb', ['control' => $controlGroup]);
 | |
| 
 | |
|         $form->addFieldPath(JPATH_PLUGINS . '/system/nrframework/fields');
 | |
|         $form->loadFile(JPATH_PLUGINS . '/system/nrframework/xml/' . $name);
 | |
| 
 | |
|         if (!is_null($data))
 | |
|         {
 | |
|             $form->bind($data);
 | |
|         }
 | |
| 
 | |
|         return $form;
 | |
|     }
 | |
| } |