private function saveRule(HeraldAdapter $adapter, $rule, $request)
 {
     $rule->setName($request->getStr('name'));
     $match_all = $request->getStr('must_match') == 'all';
     $rule->setMustMatchAll((int) $match_all);
     $repetition_policy_param = $request->getStr('repetition_policy');
     $rule->setRepetitionPolicy(HeraldRepetitionPolicyConfig::toInt($repetition_policy_param));
     $e_name = true;
     $errors = array();
     if (!strlen($rule->getName())) {
         $e_name = pht('Required');
         $errors[] = pht('Rule must have a name.');
     }
     $data = null;
     try {
         $data = phutil_json_decode($request->getStr('rule'));
     } catch (PhutilJSONParserException $ex) {
         throw new PhutilProxyException(pht('Failed to decode rule data.'), $ex);
     }
     if (!is_array($data) || !$data['conditions'] || !$data['actions']) {
         throw new Exception(pht('Failed to decode rule data.'));
     }
     $conditions = array();
     foreach ($data['conditions'] as $condition) {
         if ($condition === null) {
             // We manage this as a sparse array on the client, so may receive
             // NULL if conditions have been removed.
             continue;
         }
         $obj = new HeraldCondition();
         $obj->setFieldName($condition[0]);
         $obj->setFieldCondition($condition[1]);
         if (is_array($condition[2])) {
             $obj->setValue(array_keys($condition[2]));
         } else {
             $obj->setValue($condition[2]);
         }
         try {
             $adapter->willSaveCondition($obj);
         } catch (HeraldInvalidConditionException $ex) {
             $errors[] = $ex->getMessage();
         }
         $conditions[] = $obj;
     }
     $actions = array();
     foreach ($data['actions'] as $action) {
         if ($action === null) {
             // Sparse on the client; removals can give us NULLs.
             continue;
         }
         if (!isset($action[1])) {
             // Legitimate for any action which doesn't need a target, like
             // "Do nothing".
             $action[1] = null;
         }
         $obj = new HeraldActionRecord();
         $obj->setAction($action[0]);
         $obj->setTarget($action[1]);
         try {
             $adapter->willSaveAction($rule, $obj);
         } catch (HeraldInvalidActionException $ex) {
             $errors[] = $ex->getMessage();
         }
         $actions[] = $obj;
     }
     $rule->attachConditions($conditions);
     $rule->attachActions($actions);
     if (!$errors) {
         $edit_action = $rule->getID() ? 'edit' : 'create';
         $rule->openTransaction();
         $rule->save();
         $rule->saveConditions($conditions);
         $rule->saveActions($actions);
         $rule->saveTransaction();
     }
     return array($e_name, $errors);
 }
 private function saveRule(HeraldAdapter $adapter, $rule, $request)
 {
     $new_name = $request->getStr('name');
     $match_all = $request->getStr('must_match') == 'all';
     $repetition_policy_param = $request->getStr('repetition_policy');
     $e_name = true;
     $errors = array();
     if (!strlen($new_name)) {
         $e_name = pht('Required');
         $errors[] = pht('Rule must have a name.');
     }
     $data = null;
     try {
         $data = phutil_json_decode($request->getStr('rule'));
     } catch (PhutilJSONParserException $ex) {
         throw new PhutilProxyException(pht('Failed to decode rule data.'), $ex);
     }
     if (!is_array($data) || !$data['conditions'] || !$data['actions']) {
         throw new Exception(pht('Failed to decode rule data.'));
     }
     $conditions = array();
     foreach ($data['conditions'] as $condition) {
         if ($condition === null) {
             // We manage this as a sparse array on the client, so may receive
             // NULL if conditions have been removed.
             continue;
         }
         $obj = new HeraldCondition();
         $obj->setFieldName($condition[0]);
         $obj->setFieldCondition($condition[1]);
         if (is_array($condition[2])) {
             $obj->setValue(array_keys($condition[2]));
         } else {
             $obj->setValue($condition[2]);
         }
         try {
             $adapter->willSaveCondition($obj);
         } catch (HeraldInvalidConditionException $ex) {
             $errors[] = $ex->getMessage();
         }
         $conditions[] = $obj;
     }
     $actions = array();
     foreach ($data['actions'] as $action) {
         if ($action === null) {
             // Sparse on the client; removals can give us NULLs.
             continue;
         }
         if (!isset($action[1])) {
             // Legitimate for any action which doesn't need a target, like
             // "Do nothing".
             $action[1] = null;
         }
         $obj = new HeraldActionRecord();
         $obj->setAction($action[0]);
         $obj->setTarget($action[1]);
         try {
             $adapter->willSaveAction($rule, $obj);
         } catch (HeraldInvalidActionException $ex) {
             $errors[] = $ex->getMessage();
         }
         $actions[] = $obj;
     }
     if (!$errors) {
         $new_state = id(new HeraldRuleSerializer())->serializeRuleComponents($match_all, $conditions, $actions, $repetition_policy_param);
         $xactions = array();
         $xactions[] = id(new HeraldRuleTransaction())->setTransactionType(HeraldRuleTransaction::TYPE_EDIT)->setNewValue($new_state);
         $xactions[] = id(new HeraldRuleTransaction())->setTransactionType(HeraldRuleTransaction::TYPE_NAME)->setNewValue($new_name);
         try {
             id(new HeraldRuleEditor())->setActor($this->getViewer())->setContinueOnNoEffect(true)->setContentSourceFromRequest($request)->applyTransactions($rule, $xactions);
             return array(null, null);
         } catch (Exception $ex) {
             $errors[] = $ex->getMessage();
         }
     }
     // mutate current rule, so it would be sent to the client in the right state
     $rule->setMustMatchAll((int) $match_all);
     $rule->setName($new_name);
     $rule->setRepetitionPolicy(HeraldRepetitionPolicyConfig::toInt($repetition_policy_param));
     $rule->attachConditions($conditions);
     $rule->attachActions($actions);
     return array($e_name, $errors);
 }