From bf1074f35f99e1f1548791fba171d5865e8743c4 Mon Sep 17 00:00:00 2001
From: Jaap Jansma <jaap@edeveloper.nl>
Date: Wed, 11 Mar 2015 15:15:23 +0100
Subject: [PATCH] created form for action parameters

---
 CRM/Civirules/Action.php                      | 100 ++++++++++++++++++
 CRM/Civirules/Action/Action.php               |  61 -----------
 CRM/Civirules/Action/GroupContact.php         |  18 ----
 CRM/Civirules/BAO/Action.php                  |  46 +++++++-
 CRM/Civirules/BAO/RuleAction.php              |  19 ----
 CRM/Civirules/DAO/RuleAction.php              |   2 +-
 CRM/Civirules/Engine.php                      |  38 +++----
 CRM/Civirules/Form/Rule.php                   |   4 +
 CRM/Civirules/Form/RuleAction.php             |  14 ++-
 CRM/CivirulesActions/Form/GroupContact.php    |  79 ++++++++++++++
 CRM/CivirulesActions/GroupContact.php         |  45 ++++++++
 sql/createCiviruleRuleAction.sql              |   2 +-
 sql/createCiviruleRuleCondition.sql           |   2 +-
 .../Civirules/Form/RuleBlocks/ActionBlock.tpl |   4 +-
 .../CivirulesActions/Form/GroupContact.tpl    |  11 ++
 xml/Menu/civirules.xml                        |   6 ++
 16 files changed, 323 insertions(+), 128 deletions(-)
 create mode 100644 CRM/Civirules/Action.php
 delete mode 100644 CRM/Civirules/Action/Action.php
 delete mode 100644 CRM/Civirules/Action/GroupContact.php
 create mode 100644 CRM/CivirulesActions/Form/GroupContact.php
 create mode 100644 CRM/CivirulesActions/GroupContact.php
 create mode 100644 templates/CRM/CivirulesActions/Form/GroupContact.tpl

diff --git a/CRM/Civirules/Action.php b/CRM/Civirules/Action.php
new file mode 100644
index 0000000..eeddf31
--- /dev/null
+++ b/CRM/Civirules/Action.php
@@ -0,0 +1,100 @@
+<?php
+
+abstract class CRM_Civirules_Action {
+
+  protected $ruleAction = array();
+
+  protected $action = array();
+
+  public function setRuleActionData($ruleAction) {
+    $this->ruleAction = array();
+    if (is_array($ruleAction)) {
+      $this->ruleAction = $ruleAction;
+    }
+  }
+
+  public function setActionData($action) {
+    $this->action = $action;
+  }
+
+  /**
+   * Returns an array with parameters used for processing an action
+   *
+   * @param array $parameters
+   * @param CRM_Civirules_EventData_EventData $eventData
+   * @return array
+   */
+  protected function alterApiParameters($parameters, CRM_Civirules_EventData_EventData $eventData) {
+    //this function could be overridden in subclasses to alter parameters to meet certain criteraia
+    return $parameters;
+  }
+
+  /**
+   * Process the action
+   *
+   * @param CRM_Civirules_EventData_EventData $eventData
+   */
+  public function processAction(CRM_Civirules_EventData_EventData $eventData) {
+    $entity = $this->action['api_entity'];
+    $action = $this->action['api_action'];
+
+    $params = $this->getActionParameters();
+
+    //alter parameters by subclass
+    $params = $this->alterApiParameters($params, $eventData);
+
+    //execute the action
+    $this->executeApiAction($entity, $action, $params);
+  }
+
+  /**
+   * Executes the action
+   *
+   * This function could be overriden if needed
+   *
+   * @param $entity
+   * @param $action
+   * @param $parameters
+   */
+  protected function executeApiAction($entity, $action, $parameters) {
+    civicrm_api3($entity, $action, $parameters);
+  }
+
+  /**
+   * Convert parameters to an array of parameters
+   *
+   * @return array
+   */
+  protected function getActionParameters() {
+    $params = array();
+    if (!empty($this->ruleAction['action_params'])) {
+      $params = unserialize($this->ruleAction['action_params']);
+    }
+    return $params;
+  }
+
+  /**
+   * Returns a redirect url to extra data input from the user after adding a action
+   *
+   * Return false if you do not need extra data input
+   *
+   * @param int $ruleActionId
+   * @return bool|string
+   */
+  public function getExtraDataInputUrl($ruleActionId) {
+    return false;
+  }
+
+  /**
+   * Retruns a user friendly text explaining the condition params
+   * e.g. 'Older than 65'
+   *
+   * @return string
+   */
+  public function userFriendlyConditionParams() {
+    return '';
+  }
+
+
+
+}
\ No newline at end of file
diff --git a/CRM/Civirules/Action/Action.php b/CRM/Civirules/Action/Action.php
deleted file mode 100644
index bf296ba..0000000
--- a/CRM/Civirules/Action/Action.php
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-abstract class CRM_Civirules_Action_Action {
-
-  /**
-   * Returns an array with parameters used for processing an action
-   *
-   * @param array $parameters
-   * @param CRM_Civirules_EventData_EventData $eventData
-   * @return array
-   */
-  protected function alterApiParameters($parameters, CRM_Civirules_EventData_EventData $eventData) {
-    //this function could be overridden in subclasses to alter parameters to meet certain criteraia
-    return $parameters;
-  }
-
-  /**
-   * Process the action
-   *
-   * @param $rule_action_id
-   * @param $entity
-   * @param $action
-   * @param $parameters
-   * @param CRM_Civirules_EventData_EventData $eventData
-   */
-  public function processAction($rule_action_id, $entity, $action, $parameters, CRM_Civirules_EventData_EventData $eventData) {
-    $params = $this->convertParameters($parameters);
-
-    //alter parameters by subclass
-    $params = $this->alterApiParameters($params, $eventData);
-
-    //execute the action
-    $this->executeAction($entity, $action, $params);
-  }
-
-  /**
-   * Executes the action
-   *
-   * This function could be overriden if needed
-   *
-   * @param $entity
-   * @param $action
-   * @param $parameters
-   */
-  protected function executeAction($entity, $action, $parameters) {
-    civicrm_api3($entity, $action, $parameters);
-  }
-
-  /**
-   * Convert parameters to an array of parameters
-   *
-   * @param $action_parameters
-   * @return array
-   */
-  protected function convertParameters($action_parameters) {
-    return CRM_Civirules_Utils_Parameters::convertFromMultiline($action_parameters);
-  }
-
-
-
-}
\ No newline at end of file
diff --git a/CRM/Civirules/Action/GroupContact.php b/CRM/Civirules/Action/GroupContact.php
deleted file mode 100644
index e251cf8..0000000
--- a/CRM/Civirules/Action/GroupContact.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-class CRM_Civirules_Action_GroupContact extends CRM_Civirules_Action_Action {
-
-  /**
-   * Returns an array with parameters used for processing an action
-   *
-   * @param array $parameters
-   * @param CRM_Civirules_EventData_EventData $eventData
-   * @return array
-   */
-  protected function alterApiParameters($parameters, CRM_Civirules_EventData_EventData $eventData) {
-    //this function could be overridden in subclasses to alter parameters to meet certain criteraia
-    $parameters['contact_id'] = $eventData->getContactId();
-    return $parameters;
-  }
-
-}
\ No newline at end of file
diff --git a/CRM/Civirules/BAO/Action.php b/CRM/Civirules/BAO/Action.php
index bfd0db5..06f4860 100755
--- a/CRM/Civirules/BAO/Action.php
+++ b/CRM/Civirules/BAO/Action.php
@@ -129,4 +129,48 @@ class CRM_Civirules_BAO_Action extends CRM_Civirules_DAO_Action {
     $action->id = $actionId;
     $action->find(true);
     return $action->label;
-  }}
+  }
+
+  /**
+   * Get the action class for this condition
+   *
+   * @param $actionId
+   * @param bool $abort if true this function will throw an exception if class could not be instanciated
+   * @return CRM_Civirules_Action
+   * @throws Exception if abort is set to true and class does not exist or is not valid
+   */
+  public static function getActionObjectById($actionId, $abort=true) {
+    $action = new CRM_Civirules_BAO_Action();
+    $action->id = $actionId;
+    if (!$action->find(true)) {
+      if ($abort) {
+        throw new Exception('CiviRule could not find action');
+      }
+      return false;
+    }
+
+    $className = $action->class_name;
+    if (!class_exists($className)) {
+      if ($abort) {
+
+        throw new Exception('CiviRule action class "' . $className . '" does not exist');
+      }
+      return false;
+    }
+
+    $object = new $className();
+    if (!$object instanceof CRM_Civirules_Action) {
+      if ($abort) {
+        throw new Exception('CiviRule action class "' . $className . '" is not a subclass of CRM_Civirules_Action');
+      }
+      return false;
+    }
+
+    $actionData = array();
+    CRM_Core_DAO::storeValues($action, $actionData);
+    $object->setActionData($actionData);
+
+    return $object;
+  }
+
+}
diff --git a/CRM/Civirules/BAO/RuleAction.php b/CRM/Civirules/BAO/RuleAction.php
index c5f562e..1af1a71 100755
--- a/CRM/Civirules/BAO/RuleAction.php
+++ b/CRM/Civirules/BAO/RuleAction.php
@@ -129,23 +129,4 @@ class CRM_Civirules_BAO_RuleAction extends CRM_Civirules_DAO_RuleAction {
     }
   }
 
-  public static function getRuleActions($rule_id) {
-    $sql = "SELECT `ra`.`id`, a.`api_entity`, a.`api_action`, a.`class_name`, ra.`action_params`
-            FROM `civirule_action` `a`
-            INNER JOIN `civirule_rule_action` `ra` ON `a`.`id` = `ra`.`action_id`
-            WHERE `ra`.`rule_id` = %1";
-    $params[1] = array($rule_id, 'Integer');
-    $return = array();
-    $dao = CRM_COre_DAO::executeQuery($sql, $params);
-    while($dao->fetch()) {
-      $return[$dao->id] = array(
-        'rule_action_id' => $dao->id,
-        'api_entity' => $dao->api_entity,
-        'api_action' => $dao->api_action,
-        'api_parameters' => $dao->action_params,
-        'class_name' => $dao->class_name
-      );
-    }
-    return $return;
-  }
 }
\ No newline at end of file
diff --git a/CRM/Civirules/DAO/RuleAction.php b/CRM/Civirules/DAO/RuleAction.php
index 2c8ef05..5571ce7 100755
--- a/CRM/Civirules/DAO/RuleAction.php
+++ b/CRM/Civirules/DAO/RuleAction.php
@@ -44,7 +44,7 @@ class CRM_Civirules_DAO_RuleAction extends CRM_Core_DAO {
         ),
         'action_params' => array(
           'name' => 'action_params',
-          'type' => CRM_Utils_Type::T_BLOB
+          'type' => CRM_Utils_Type::T_TEXT
         ),
         'is_active' => array(
           'name' => 'is_active',
diff --git a/CRM/Civirules/Engine.php b/CRM/Civirules/Engine.php
index 4d7e6f8..4c432ff 100644
--- a/CRM/Civirules/Engine.php
+++ b/CRM/Civirules/Engine.php
@@ -8,42 +8,38 @@ class CRM_Civirules_Engine {
    * The trigger will check the conditions and if conditions are valid then the actions are executed
    *
    * @param CRM_Civirules_EventData_EventData $eventData
-   * @param $rule_id
+   * @param $ruleId
+   * @param $eventId
    */
-  public static function triggerRule(CRM_Civirules_EventData_EventData $eventData, $rule_id, $event_id) {
-    $eventData->setEventId($event_id);
-    $eventData->setRuleId($rule_id);
+  public static function triggerRule(CRM_Civirules_EventData_EventData $eventData, $ruleId, $eventId) {
+    $eventData->setEventId($eventId);
+    $eventData->setRuleId($ruleId);
 
-    $isRuleValid = self::areConditionsValid($eventData, $rule_id);
+    $isRuleValid = self::areConditionsValid($eventData, $ruleId);
 
     if ($isRuleValid) {
-      self::executeActions($eventData, $rule_id);
+      self::executeActions($eventData, $ruleId);
     }
   }
 
-  protected static function executeActions(CRM_Civirules_EventData_EventData $eventData, $rule_id) {
-    $ruleActions = CRM_Civirules_BAO_RuleAction::getRuleActions($rule_id);
-    foreach($ruleActions as $ruleAction) {
+  protected static function executeActions(CRM_Civirules_EventData_EventData $eventData, $ruleId) {
+    $actionParams = array(
+      'rule_id' => $ruleId
+    );
+    $ruleActions = CRM_Civirules_BAO_RuleAction::getValues($actionParams);
+    foreach ($ruleActions as $ruleAction) {
       self::executeAction($eventData, $ruleAction);
     }
   }
 
   protected static function executeAction(CRM_Civirules_EventData_EventData $eventData, $ruleAction) {
-    $className = $ruleAction['class_name'];
-    if (!class_exists($className)) {
-      return;
-    }
-
-    $object = new $className();
-    if (!$object instanceof CRM_Civirules_Action_Action) {
+    $object = CRM_Civirules_BAO_Action::getActionObjectById($ruleAction['action_id'], true);
+    if (!$object) {
       return;
     }
 
-    $rule_action_id = $ruleAction['rule_action_id'];
-    $entity = $ruleAction['api_entity'];
-    $action = $ruleAction['api_action'];
-    $parameters = $ruleAction['api_parameters'];
-    $object->processAction($rule_action_id, $entity, $action, $parameters, $eventData);
+    $object->setRuleActionData($ruleAction);
+    $object->processAction($eventData);
   }
 
   protected static function areConditionsValid(CRM_Civirules_EventData_EventData $eventData, $rule_id) {
diff --git a/CRM/Civirules/Form/Rule.php b/CRM/Civirules/Form/Rule.php
index 7c61acf..ebef293 100755
--- a/CRM/Civirules/Form/Rule.php
+++ b/CRM/Civirules/Form/Rule.php
@@ -273,6 +273,10 @@ class CRM_Civirules_Form_Rule extends CRM_Core_Form {
       $ruleActions[$ruleActionId]['label'] =
         CRM_Civirules_BAO_Action::getActionLabelWithId($ruleAction['action_id']);
       $ruleActions[$ruleActionId]['actions'] = $this->setRuleActionActions($ruleActionId);
+
+      $actionClass = CRM_Civirules_BAO_Action::getActionObjectById($ruleAction['action_id']);
+      $actionClass->setRuleActionData($ruleAction);
+      $ruleActions[$ruleActionId]['formattedConditionParams'] = $actionClass->userFriendlyConditionParams();
     }
     return $ruleActions;
   }
diff --git a/CRM/Civirules/Form/RuleAction.php b/CRM/Civirules/Form/RuleAction.php
index 6e4efd4..894ad6f 100644
--- a/CRM/Civirules/Form/RuleAction.php
+++ b/CRM/Civirules/Form/RuleAction.php
@@ -47,15 +47,23 @@ class CRM_Civirules_Form_RuleAction extends CRM_Core_Form {
    * @access public
    */
   function postProcess() {
-    $session = CRM_Core_Session::singleton();
+
     $saveParams = array(
       'rule_id' => $this->_submitValues['rule_id'],
       'action_id' => $this->_submitValues['rule_action_select']
     );
-    CRM_Civirules_BAO_RuleAction::add($saveParams);
-    $redirectUrl = CRM_Utils_System::url('civicrm/civirule/form/rule', 'action=update&id='.$this->_submitValues['rule_id'], TRUE);
+    $ruleAction = CRM_Civirules_BAO_RuleAction::add($saveParams);
+
+    $session = CRM_Core_Session::singleton();
     $session->setStatus('Action added to CiviRule '.CRM_Civirules_BAO_Rule::getRuleLabelWithId($this->_submitValues['rule_id']),
       'Action added', 'success');
+
+    $action = CRM_Civirules_BAO_Action::getActionObjectById($ruleAction['action_id'], true);
+    $redirectUrl = $action->getExtraDataInputUrl($ruleAction['id']);
+    if (empty($redirectUrl)) {
+      $redirectUrl = CRM_Utils_System::url('civicrm/civirule/form/rule', 'action=update&id=' . $this->_submitValues['rule_id'], TRUE);
+    }
+
     CRM_Utils_System::redirect($redirectUrl);
   }
 
diff --git a/CRM/CivirulesActions/Form/GroupContact.php b/CRM/CivirulesActions/Form/GroupContact.php
new file mode 100644
index 0000000..756bf5c
--- /dev/null
+++ b/CRM/CivirulesActions/Form/GroupContact.php
@@ -0,0 +1,79 @@
+<?php
+
+class CRM_CivirulesActions_Form_GroupContact extends CRM_Core_Form {
+
+  protected $ruleActionId = false;
+
+  protected $ruleAction;
+
+  protected $action;
+
+  function preProcess() {
+    $this->ruleActionId = CRM_Utils_Request::retrieve('rule_action_id', 'Integer');
+
+    $this->ruleAction = new CRM_Civirules_BAO_RuleAction();
+    $this->action = new CRM_Civirules_BAO_Action();
+    $this->ruleAction->id = $this->ruleActionId;
+    if ($this->ruleAction->find(true)) {
+      $this->action->id = $this->ruleAction->action_id;
+      if (!$this->action->find(true)) {
+        throw new Exception('CiviRules Could not find action with id '.$this->ruleAction->action_id);
+      }
+    } else {
+      throw new Exception('CiviRules Could not find rule action with id '.$this->ruleActionId);
+    }
+
+    parent::preProcess();
+  }
+
+  protected function getGroups() {
+    return array('' => ts('-- please select --')) + CRM_Contact_BAO_GroupContact::getGroupList();
+  }
+
+  function buildQuickForm() {
+    $this->setFormTitle();
+
+    $this->add('hidden', 'rule_action_id');
+
+    $this->add('select', 'group_id', ts('Group'), $this->getGroups(), true);
+
+    $this->addButtons(array(
+      array('type' => 'next', 'name' => ts('Save'), 'isDefault' => TRUE,),
+      array('type' => 'cancel', 'name' => ts('Cancel'))));
+  }
+
+  public function setDefaultValues() {
+    $data = array();
+    $defaultValues = array();
+    $defaultValues['rule_action_id'] = $this->ruleActionId;
+    if (!empty($this->ruleAction->action_params)) {
+      $data = unserialize($this->ruleCondition->action_params);
+    }
+    if (!empty($data['group_id'])) {
+      $defaultValues['group_id'] = $data['group_id'];
+    }
+    return $defaultValues;
+  }
+
+  public function postProcess() {
+    $data['group_id'] = $this->_submitValues['group_id'];
+
+    $ruleAction = new CRM_Civirules_BAO_RuleAction();
+    $ruleAction->id = $this->ruleActionId;
+    $ruleAction->action_params = serialize($data);
+    $ruleAction->save();
+
+    $session = CRM_Core_Session::singleton();
+    $session->setStatus('Action '.$this->action->label.' parameters updated to CiviRule '.CRM_Civirules_BAO_Rule::getRuleLabelWithId($this->ruleAction->rule_id),
+      'Action parameters updated', 'success');
+
+    $redirectUrl = CRM_Utils_System::url('civicrm/civirule/form/rule', 'action=update&id='.$this->ruleAction->rule_id, TRUE);
+    CRM_Utils_System::redirect($redirectUrl);  }
+
+  protected function setFormTitle() {
+    $title = 'CiviRules Edit Action parameters';
+    $this->assign('ruleActionHeader', 'Edit action '.$this->action->label.' of CiviRule '.CRM_Civirules_BAO_Rule::getRuleLabelWithId($this->ruleAction->rule_id));
+    CRM_Utils_System::setTitle($title);
+  }
+
+}
\ No newline at end of file
diff --git a/CRM/CivirulesActions/GroupContact.php b/CRM/CivirulesActions/GroupContact.php
new file mode 100644
index 0000000..992b38f
--- /dev/null
+++ b/CRM/CivirulesActions/GroupContact.php
@@ -0,0 +1,45 @@
+<?php
+
+class CRM_CivirulesActions_GroupContact extends CRM_Civirules_Action {
+
+  /**
+   * Returns an array with parameters used for processing an action
+   *
+   * @param array $parameters
+   * @param CRM_Civirules_EventData_EventData $eventData
+   * @return array
+   */
+  protected function alterApiParameters($parameters, CRM_Civirules_EventData_EventData $eventData) {
+    //this function could be overridden in subclasses to alter parameters to meet certain criteraia
+    $parameters['contact_id'] = $eventData->getContactId();
+    return $parameters;
+  }
+
+  /**
+   * Returns a redirect url to extra data input from the user after adding a action
+   *
+   * Return false if you do not need extra data input
+   *
+   * @param int $ruleActionId
+   * @return bool|string
+   */
+  public function getExtraDataInputUrl($ruleActionId) {
+    return CRM_Utils_System::url('civicrm/civirule/form/action/groupcontact', 'rule_action_id='.$ruleActionId);
+  }
+
+  /**
+   * Retruns a user friendly text explaining the condition params
+   * e.g. 'Older than 65'
+   *
+   * @return string
+   */
+  public function userFriendlyConditionParams() {
+    $params = $this->getActionParameters();
+    if (!empty($params['group_id'])) {
+      $group = civicrm_api3('Group', 'getvalue', array('return' => 'title', 'id' => $params['group_id']));
+      return ts('Add contact to group %1', array(1 => $group));
+    }
+    return '';
+  }
+
+}
\ No newline at end of file
diff --git a/sql/createCiviruleRuleAction.sql b/sql/createCiviruleRuleAction.sql
index 5b3ed7f..f869c12 100755
--- a/sql/createCiviruleRuleAction.sql
+++ b/sql/createCiviruleRuleAction.sql
@@ -2,7 +2,7 @@ CREATE TABLE IF NOT EXISTS civirule_rule_action (
   id INT UNSIGNED NOT NULL AUTO_INCREMENT,
   rule_id INT UNSIGNED NULL,
   action_id INT UNSIGNED NULL,
-  action_params BLOB NULL,
+  action_params TEXT NULL,
   is_active TINYINT NULL DEFAULT 1,
   UNIQUE INDEX id_UNIQUE (id ASC),
   INDEX fk_ra_rule_idx (rule_id ASC),
diff --git a/sql/createCiviruleRuleCondition.sql b/sql/createCiviruleRuleCondition.sql
index 4d5965b..2e546e5 100755
--- a/sql/createCiviruleRuleCondition.sql
+++ b/sql/createCiviruleRuleCondition.sql
@@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS civirule_rule_condition (
   rule_id INT UNSIGNED NULL,
   condition_link VARCHAR(3) NULL,
   condition_id INT UNSIGNED NULL,
-  condition_params TEXT,
+  condition_params TEXT NULL,
   is_active TINYINT NULL DEFAULT 1,
   PRIMARY KEY (id),
   UNIQUE INDEX id_UNIQUE (id ASC),
diff --git a/templates/CRM/Civirules/Form/RuleBlocks/ActionBlock.tpl b/templates/CRM/Civirules/Form/RuleBlocks/ActionBlock.tpl
index 10b57d9..57cea17 100755
--- a/templates/CRM/Civirules/Form/RuleBlocks/ActionBlock.tpl
+++ b/templates/CRM/Civirules/Form/RuleBlocks/ActionBlock.tpl
@@ -19,8 +19,8 @@
             <tr id="row_{$rowNumber}" class={$rowClass}>
               <td hidden="1" id="ruleActionId">{$ruleAction.id}</td>
               <td>{$ruleAction.label}</td>
-              {if !empty($ruleAction.action_params)}
-                <td>{$ruleAction.action_params}</td>
+              {if !empty($ruleAction.formattedConditionParams)}
+                <td>{$ruleAction.formattedConditionParams}</td>
               {else}
                 <td>&nbsp;</td>
               {/if}
diff --git a/templates/CRM/CivirulesActions/Form/GroupContact.tpl b/templates/CRM/CivirulesActions/Form/GroupContact.tpl
new file mode 100644
index 0000000..80ad325
--- /dev/null
+++ b/templates/CRM/CivirulesActions/Form/GroupContact.tpl
@@ -0,0 +1,11 @@
+<h3>{$ruleActionHeader}</h3>
+<div class="crm-block crm-form-block crm-civirule-rule_action-block-group-contact">
+    <div class="crm-section">
+        <div class="label">{$form.group_id.label}</div>
+        <div class="content">{$form.group_id.html}</div>
+        <div class="clear"></div>
+    </div>
+</div>
+<div class="crm-submit-buttons">
+    {include file="CRM/common/formButtons.tpl" location="bottom"}
+</div>
\ No newline at end of file
diff --git a/xml/Menu/civirules.xml b/xml/Menu/civirules.xml
index 8815e6b..32cf78d 100755
--- a/xml/Menu/civirules.xml
+++ b/xml/Menu/civirules.xml
@@ -30,4 +30,10 @@
       <title>Value comparison</title>
       <access_arguments>access CiviCRM</access_arguments>
   </item>
+    <item>
+        <path>civicrm/civirule/form/action/groupcontact</path>
+        <page_callback>CRM_CivirulesActions_Form_GroupContact</page_callback>
+        <title>Group contact</title>
+        <access_arguments>access CiviCRM</access_arguments>
+    </item>
 </menu>
-- 
GitLab