<commit_before><?php

/*
 * Copyright 2011 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

class HeraldRuleController extends HeraldController {

  private $id;

  public function willProcessRequest(array $data) {
    $this->id = (int)idx($data, 'id');
  }

  public function processRequest() {

    $request = $this->getRequest();
    $user = $request->getUser();

    $content_type_map = HeraldContentTypeConfig::getContentTypeMap();

    if ($this->id) {
      $rule = id(new HeraldRule())->load($this->id);
      if (!$rule) {
        return new Aphront404Response();
      }
      if ($rule->getAuthorPHID() != $user->getPHID()) {
        throw new Exception("You don't own this rule and can't edit it.");
      }
    } else {
      $rule = new HeraldRule();
      $rule->setAuthorPHID($user->getPHID());
      $rule->setMustMatchAll(true);

      $type = $request->getStr('type');
      if (!isset($content_type_map[$type])) {
        $type = HeraldContentTypeConfig::CONTENT_TYPE_DIFFERENTIAL;
      }
      $rule->setContentType($type);
    }

    $local_version = id(new HeraldRule())->getConfigVersion();
    if ($rule->getConfigVersion() > $local_version) {
      throw new Exception(
        "This rule was created with a newer version of Herald. You can not ".
        "view or edit it in this older version. Try dev or wait for a push.");
    }

    // Upgrade rule version to our version, since we might add newly-defined
    // conditions, etc.
    $rule->setConfigVersion($local_version);

    $rule_conditions = $rule->loadConditions();
    $rule_actions = $rule->loadActions();

    $rule->attachConditions($rule_conditions);
    $rule->attachActions($rule_actions);

    $e_name = true;
    $errors = array();
    if ($request->isFormPost() && $request->getStr('save')) {
      $rule->setName($request->getStr('name'));
      $rule->setMustMatchAll(($request->getStr('must_match') == 'all'));

      $repetition_policy_param = $request->getStr('repetition_policy');
      $rule->setRepetitionPolicy(
        HeraldRepetitionPolicyConfig::toInt($repetition_policy_param)
      );

      if (!strlen($rule->getName())) {
        $e_name = "Required";
        $errors[] = "Rule must have a name.";
      }

      $data = json_decode($request->getStr('rule'), true);
      if (!is_array($data) ||
          !$data['conditions'] ||
          !$data['actions']) {
        throw new Exception("Failed to decode rule data.");
      }

      $conditions = array();
      foreach ($data['conditions'] as $condition) {
        $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]);
        }

        $cond_type = $obj->getFieldCondition();

        if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP) {
          if (@preg_match($obj->getValue(), '') === false) {
            $errors[] =
              'The regular expression "'.$obj->getValue().'" is not valid. '.
              'Regular expressions must have enclosing characters (e.g. '.
              '"@/path/to/file@", not "/path/to/file") and be syntactically '.
              'correct.';
          }
        }

        if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP_PAIR) {
          $json = json_decode($obj->getValue(), true);
          if (!is_array($json)) {
            $errors[] =
              'The regular expression pair "'.$obj->getValue().'" is not '.
              'valid JSON. Enter a valid JSON array with two elements.';
          } else {
            if (count($json) != 2) {
              $errors[] =
                'The regular expression pair "'.$obj->getValue().'" must have '.
                'exactly two elements.';
            } else {
              $key_regexp = array_shift($json);
              $val_regexp = array_shift($json);

              if (@preg_match($key_regexp, '') === false) {
                $errors[] =
                  'The first regexp, "'.$key_regexp.'" in the regexp pair '.
                  'is not a valid regexp.';
              }
              if (@preg_match($val_regexp, '') === false) {
                $errors[] =
                  'The second regexp, "'.$val_regexp.'" in the regexp pair '.
                  'is not a valid regexp.';
              }
            }
          }
        }

        $conditions[] = $obj;
      }

      $actions = array();
      foreach ($data['actions'] as $action) {
        $obj = new HeraldAction();
        $obj->setAction($action[0]);

        if (!isset($action[1])) {
          // Legitimate for any action which doesn't need a target, like
          // "Do nothing".
          $action[1] = null;
        }

        if (is_array($action[1])) {
          $obj->setTarget(array_keys($action[1]));
        } else {
          $obj->setTarget($action[1]);
        }

        $actions[] = $obj;
      }

      $rule->attachConditions($conditions);<commit_msg>Fix Herald to accept new JSON encoding of sparse arrays<commit_after><?php

/*
 * Copyright 2011 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

class HeraldRuleController extends HeraldController {

  private $id;

  public function willProcessRequest(array $data) {
    $this->id = (int)idx($data, 'id');
  }

  public function processRequest() {

    $request = $this->getRequest();
    $user = $request->getUser();

    $content_type_map = HeraldContentTypeConfig::getContentTypeMap();

    if ($this->id) {
      $rule = id(new HeraldRule())->load($this->id);
      if (!$rule) {
        return new Aphront404Response();
      }
      if ($rule->getAuthorPHID() != $user->getPHID()) {
        throw new Exception("You don't own this rule and can't edit it.");
      }
    } else {
      $rule = new HeraldRule();
      $rule->setAuthorPHID($user->getPHID());
      $rule->setMustMatchAll(true);

      $type = $request->getStr('type');
      if (!isset($content_type_map[$type])) {
        $type = HeraldContentTypeConfig::CONTENT_TYPE_DIFFERENTIAL;
      }
      $rule->setContentType($type);
    }

    $local_version = id(new HeraldRule())->getConfigVersion();
    if ($rule->getConfigVersion() > $local_version) {
      throw new Exception(
        "This rule was created with a newer version of Herald. You can not ".
        "view or edit it in this older version. Try dev or wait for a push.");
    }

    // Upgrade rule version to our version, since we might add newly-defined
    // conditions, etc.
    $rule->setConfigVersion($local_version);

    $rule_conditions = $rule->loadConditions();
    $rule_actions = $rule->loadActions();

    $rule->attachConditions($rule_conditions);
    $rule->attachActions($rule_actions);

    $e_name = true;
    $errors = array();
    if ($request->isFormPost() && $request->getStr('save')) {
      $rule->setName($request->getStr('name'));
      $rule->setMustMatchAll(($request->getStr('must_match') == 'all'));

      $repetition_policy_param = $request->getStr('repetition_policy');
      $rule->setRepetitionPolicy(
        HeraldRepetitionPolicyConfig::toInt($repetition_policy_param)
      );

      if (!strlen($rule->getName())) {
        $e_name = "Required";
        $errors[] = "Rule must have a name.";
      }

      $data = json_decode($request->getStr('rule'), true);
      if (!is_array($data) ||
          !$data['conditions'] ||
          !$data['actions']) {
        throw new Exception("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]);
        }

        $cond_type = $obj->getFieldCondition();

        if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP) {
          if (@preg_match($obj->getValue(), '') === false) {
            $errors[] =
              'The regular expression "'.$obj->getValue().'" is not valid. '.
              'Regular expressions must have enclosing characters (e.g. '.
              '"@/path/to/file@", not "/path/to/file") and be syntactically '.
              'correct.';
          }
        }

        if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP_PAIR) {
          $json = json_decode($obj->getValue(), true);
          if (!is_array($json)) {
            $errors[] =
              'The regular expression pair "'.$obj->getValue().'" is not '.
              'valid JSON. Enter a valid JSON array with two elements.';
          } else {
            if (count($json) != 2) {
              $errors[] =
                'The regular expression pair "'.$obj->getValue().'" must have '.
                'exactly two elements.';
            } else {
              $key_regexp = array_shift($json);
              $val_regexp = array_shift($json);

              if (@preg_match($key_regexp, '') === false) {
                $errors[] =
                  'The first regexp, "'.$key_regexp.'" in the regexp pair '.
                  'is not a valid regexp.';
              }
              if (@preg_match($val_regexp, '') === false) {
                $errors[] =
                  'The second regexp, "'.$val_regexp.'" in the regexp pair '.
                  'is not a valid regexp.';
              }
            }
          }
        }

        $conditions[] = $obj;
      }

      $actions = array();
      foreach ($data['actions'] as $action) {
        if ($action === null) {
          // Sparse on the client; removals can give us NULLs.
          continue;
        }

        $obj = new HeraldAction();
        $obj->setAction($action[0]);

        if (!isset($action[1])) {
          // Legitimate for any action which doesn't need a target, like
          // "Do nothing".
          $action[1] = null;
        }

        if (is_array($action[1])) {
          $obj->setTarget(array_keys($action[1]));
        } else {
          $obj->setTarget($action[1]);
        }

        $actions[] = $obj;
      }

      $rule->attachConditions($conditions);
{'fraction_non_alphanumeric': 0.1494661921708185, 'fraction_numerical': 0.00399270896623557, 'mean_word_length': 2.231977559607293, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 93, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1889701', 'n_tokens_mistral': 3316, 'n_tokens_neox': 3195, 'n_words': 842}