View source  
  <?php
interface IBotchaRecipebookController {
  public function getRecipebook($id = 'default', $create = TRUE);
  public function getRecipebooks($reset = FALSE);
  public function save($recipebook);
  public function delete($recipebook);
}
class BotchaRecipebookController extends Controller implements IBotchaRecipebookController {
  protected $app_name = 'Botcha';
  protected $controller_type = Botcha::CONTROLLER_TYPE_RECIPEBOOK;
  
  protected function getModel() {
    return parent::getModel();
  }
  public function getRecipebook($id = 'default', $create = TRUE) {
    $none = TRUE;
    if ($id != 'none') {
      $rb = $this
        ->getModel()
        ->getRecipebook($id);
      if ($rb || $create) {
        $none = FALSE;
      }
    }
    if ($none) {
      $recipebook = new BotchaRecipebookNone($id);
    }
    else {
      $recipebook = new BotchaRecipebook($id);
      if ($rb) {
        $recipebook
          ->setTitle($rb->title)
          ->setDescription($rb->description);
      }
      $recipebook
        ->getForms();
      $recipebook
        ->getRecipes();
    }
    return $recipebook;
  }
  public function getRecipebooks($reset = FALSE, $withNone = FALSE) {
    
    $recipebooks = $this
      ->getModel()
      ->getRecipebooks();
    if ($withNone) {
      
      $recipebook_none = $this
        ->getRecipebook('none');
      $recipebooks[$recipebook_none->id] = $recipebook_none;
    }
    return $recipebooks;
  }
  public function save($recipebook) {
    
    $this
      ->getModel()
      ->save($recipebook);
    
    return $this
      ->getRecipebook($recipebook->id, FALSE);
  }
  public function delete($recipebook) {
    
    $this
      ->getModel()
      ->delete($recipebook);
  }
}
class BotchaRecipebook {
  
  public $id;
  
  public $title;
  
  public $description;
  
  
  protected $recipes;
  
  
  protected $forms;
  public function __construct($id) {
    $this->id = $id;
  }
  public function isApplicable($form, $form_state) {
    $form_id = $form['form_id']['#value'];
    $isApplicable = FALSE;
    if (!user_access('skip BOTCHA')) {
      $isApplicable = TRUE;
    }
    switch ($form_id) {
      case 'user_register':
        
        if (FALSE === strpos($form['#action'], 'user/register')) {
          if (!variable_get('botcha_allow_on_admin_pages', FALSE)) {
            $isApplicable = FALSE;
          }
        }
        break;
    }
    return $isApplicable;
  }
  public function setTitle($title) {
    $this->title = $title;
    return $this;
  }
  public function getTitle() {
    return $this->title;
  }
  public function setDescription($description) {
    $this->description = $description;
    return $this;
  }
  public function getDescription() {
    return $this->description;
  }
  public function setRecipe($recipe_id) {
    $this->recipes[$recipe_id] = $recipe_id;
    return $this;
  }
  public function unsetRecipe($recipe_id) {
    unset($this->recipes[$recipe_id]);
    return $this;
  }
  
  public function getRecipes() {
    if (!isset($this->recipes)) {
      $rs = BotchaModel::getRecipebooksRecipes(array(
        'mode' => 'recipe',
        'recipebooks' => $this->id,
      ));
      foreach ($rs as $recipe_id) {
        $this
          ->setRecipe($recipe_id);
      }
    }
    return (array) $this->recipes;
  }
  public function setForm($form_id) {
    $this->forms[$form_id] = $form_id;
    return $this;
  }
  public function unsetForm($form_id) {
    unset($this->forms[$form_id]);
    return $this;
  }
  
  public function getForms() {
    if (!isset($this->forms)) {
      $fs = BotchaModel::getRecipebooksForms(array(
        'mode' => 'form',
        'recipebooks' => $this->id,
      ));
      foreach ($fs as $form_id) {
        $this
          ->setForm($form_id);
      }
    }
    return (array) $this->forms;
  }
  
  public function handle($result, $form, $form_state, $is_spam) {
    $recipes_spam = array_intersect($is_spam, array_fill_keys(array_keys($is_spam), TRUE));
    $recipes_spam_count = count($recipes_spam);
    $recipes_success = array_intersect($is_spam, array_fill_keys(array_keys($is_spam), FALSE));
    $recipes_success_count = count($recipes_success);
    
    switch ($result) {
      case 'success':
        variable_set('botcha_form_passed_counter', $recipes_success_count);
        
        if (BOTCHA_LOGLEVEL >= 3) {
          watchdog(BOTCHA_LOG, '!form_id post approved by BOTCHA.!more', array(
            '!form_id' => $form['form_id']['#value'],
            '!more' => '' . (BOTCHA_LOGLEVEL >= 3 ? ' Checked ' . count($this
              ->getRecipes()) . ' botchas (' . join(', ', $this
              ->getRecipes()) . ').' : '') . (BOTCHA_LOGLEVEL >= 5 ? '<br /><br />' . 'POST=<pre>' . print_r(_botcha_filter_form_values_log($_POST), 1) . '</pre>' : '') . (BOTCHA_LOGLEVEL >= 5 ? '<br /><br />' . 'GET=<pre>' . print_r(_botcha_filter_form_values_log($_GET), 1) . '</pre>' : '') . (BOTCHA_LOGLEVEL >= 5 ? '<br /><br />' . 'SERVER=<pre>' . print_r($_SERVER, 1) . '</pre>' : '') . (BOTCHA_LOGLEVEL >= 5 ? '<br /><br />' . ' form=<pre>' . print_r(_botcha_filter_form_log($form), 1) . '</pre>' : '') . (BOTCHA_LOGLEVEL >= 5 ? '<br /><br />' . ' values=<pre>' . print_r(_botcha_filter_form_values_log($form_state['values']), 1) . '</pre>' : ''),
          ), WATCHDOG_INFO);
        }
        $rules_event_name = 'botcha_form_approved';
        break;
      case 'spam':
      default:
        variable_set('botcha_form_blocked_counter', $recipes_spam_count);
        
        if (BOTCHA_LOGLEVEL >= 1) {
          watchdog(BOTCHA_LOG, '!form_id post blocked by BOTCHA: submission looks like from a spambot.!more', array(
            '!form_id' => $form['form_id']['#value'],
            '!more' => '' . (BOTCHA_LOGLEVEL >= 2 ? '<br /><br />' . 'Failed ' . $recipes_spam_count . ' of ' . count($this
              ->getRecipes()) . ' recipes [' . implode(', ', array_keys($recipes_spam)) . '] from "' . $this->id . '" recipe book.' : '') . (BOTCHA_LOGLEVEL >= 5 ? '<br /><br />' . 'POST=<pre>' . print_r(_botcha_filter_form_values_log($_POST), 1) . '</pre>' : '') . (BOTCHA_LOGLEVEL >= 5 ? '<br /><br />' . 'GET=<pre>' . print_r(_botcha_filter_form_values_log($_GET), 1) . '</pre>' : '') . (BOTCHA_LOGLEVEL >= 5 ? '<br /><br />' . 'SERVER=<pre>' . print_r($_SERVER, 1) . '</pre>' : '') . (BOTCHA_LOGLEVEL >= 5 ? '<br /><br />' . ' values=<pre>' . print_r(_botcha_filter_form_values_log($form_state['values']), 1) . '</pre>' : ''),
          ), WATCHDOG_WARNING);
        }
        $rules_event_name = 'botcha_form_rejected';
        break;
    }
    
    if (module_exists('rules')) {
      $arguments = array(
        
        'form_id' => $form['form_id']['#value'],
        'total_recipes' => count($this
          ->getRecipes()),
        'passed_recipes' => $recipes_success_count,
        'passed_recipes_names' => join(', ', array_keys($recipes_success)),
        
        
        
        
        'fail' => 'FAIL',
        'failed_field' => 'mail',
      );
      
      rules_invoke_event($rules_event_name, $arguments);
    }
  }
  
  public function isSpam($form, $form_state, $is_spam) {
    
    return count($is_spam) ? (bool) array_diff($is_spam, array_fill(0, count($is_spam), FALSE)) : FALSE;
  }
  public function apply(&$form, &$form_state) {
    
    $form += array(
      '#input' => TRUE,
    );
    $form['#process'][] = 'botcha_fprocess';
    
    
    if (is_array($form['#validate'])) {
      
      array_unshift($form['#validate'], '');
      $form['#validate'][0] = 'botcha_formValidate';
    }
    else {
      $form['#validate'] = array(
        'botcha_formValidate',
      );
    }
    
    $form_state['#botcha'] = $this->id;
  }
}
class BotchaRecipebookNone extends BotchaRecipebook {
  
  public function __construct($id = NULL) {
    $this->id = !empty($id) ? $id : 'none';
    $this
      ->setTitle('None');
    $this
      ->setDescription('Help class: "Null object" pattern.');
  }
}