You are here

class Botcha in BOTCHA Spam Prevention 7.2

Same name and namespace in other branches
  1. 6.2 controller/botcha.controller.inc \Botcha
  2. 6.3 controller/application/botcha.application.controller.inc \Botcha
  3. 7.3 controller/application/botcha.application.controller.inc \Botcha

Singleton realization of botcha application.

Hierarchy

Expanded class hierarchy of Botcha

File

controller/botcha.controller.inc, line 20
Contains Botcha class.

View source
class Botcha {
  const BOTCHA_ADMIN_PATH = 'admin/config/people/botcha';
  const BOTCHA_SESSION_FORMS = 'botcha_forms';
  const BOTCHA_SESSION_RECIPEBOOKS = 'botcha_recipebooks';
  const BOTCHA_SESSION_RECIPES = 'botcha_recipes';

  // Protect from any way of creation: initialize, cloning and unserialize.
  private function __construct() {

    /* ... @return Singleton */
  }
  private function __clone() {

    /* ... @return Singleton */
  }
  private function __wakeup() {

    /* ... @return Singleton */
  }

  /**
   * Get admin form.
   */
  public static function getAdminForm($form_name) {
    $form = array();
    switch ($form_name) {
      case 'form_edit':
        list($form_name, $form_state, $botcha_form) = func_get_args();

        // Determine default values depending on whether we add or edit form.
        $edit = !empty($botcha_form);
        if ($edit) {
          if ($botcha_form instanceof BotchaFormNone) {
            drupal_goto(Botcha::BOTCHA_ADMIN_PATH . '/form/add', array(
              'query' => array(
                'botcha_form_id' => $botcha_form->id,
              ),
            ));
          }
          $operation = 'edit';
          $isEnabled = $botcha_form
            ->isEnabled();
          $recipebook = $botcha_form
            ->getRecipebook();
          $botcha_form_id_default = check_plain($botcha_form->id);
          $button = t('Save');
        }
        else {
          $operation = 'add';
          $isEnabled = TRUE;

          // 'default' will be selected.
          $recipebook = Botcha::getRecipebook();
          $query_params = drupal_get_query_parameters();
          $botcha_form_id_default = !empty($query_params['botcha_form_id']) ? $query_params['botcha_form_id'] : NULL;
          $button = t('Add');
        }

        // Insert a field that allows to understand whether it is creation or edition form submission.
        $form['botcha_operation'] = array(
          '#type' => 'value',
          '#value' => $operation,
        );

        // Determine whether the form enabled or not.
        $form['botcha_enabled'] = array(
          '#type' => 'checkbox',
          '#title' => t('Enabled'),
          '#description' => t('Enable BOTCHA protection for the form.'),
          '#default_value' => $isEnabled,
        );

        // Use passed as a parameter form id.
        $form['botcha_form_id'] = array(
          '#type' => 'machine_name',
          '#title' => t('Form ID'),
          '#description' => t('The Drupal form_id of the form to add the BOTCHA protection to.'),
          '#default_value' => $botcha_form_id_default,
          '#disabled' => $edit,
          '#maxlength' => 128,
          '#machine_name' => array(
            'exists' => 'botcha_form_exists',
          ),
        );
        $options = array();
        $recipebooks = Botcha::getRecipebooks(TRUE);
        foreach ($recipebooks as $rb) {
          $options[$rb->id] = $rb->title;
        }
        $form['botcha_form_recipebook'] = array(
          '#type' => 'select',
          '#title' => t('Recipe book'),
          '#description' => t('Recipe book to apply to the form.'),
          '#default_value' => $recipebook->id,
          '#options' => $options,
        );
        $form['submit'] = array(
          '#type' => 'submit',
          '#value' => $button,
        );
        break;
      case 'form_list':
        if (module_exists('captcha')) {

          // Checkbox to put BOTCHA on same forms as CAPTCHA.
          $form['botcha_form_protection']['botcha_on_captcha_forms'] = array(
            '#type' => 'checkbox',
            '#title' => t('Add BOTCHA to forms selected for CAPTCHA'),
            '#default_value' => variable_get('botcha_on_captcha_forms', TRUE),
            '#description' => t('This option makes it easy to manage BOTCHA settings on forms. When enabled, all forms that are configured to have CAPTCHA on them (<a href="@captcha">see configuration</a>) will also be selected for BOTCHA protection.!more', array(
              '@captcha' => url('admin/config/people/captcha'),
              '!more' => module_exists('captcha') ? '' : '<br />' . t('<b>Note:</b> <a href="@captcha_home">CAPTCHA module</a> is not installed. This setting will have no effect.', array(
                '@captcha_home' => 'http://drupal.org/project/captcha',
              )),
            )),
          );
        }

        // List known form_ids.
        $form['botcha_form_protection']['botcha_form_id_overview'] = array(
          '#theme' => 'botcha_forms_form_botcha_forms',
          '#tree' => TRUE,
        );

        // @todo Remove it.

        //$form['botcha_form_protection']['botcha_form_id_overview']['#header'] = array('form_id', t('Recipe book'), t('Operations'));
        $form['botcha_form_protection']['botcha_form_id_overview']['#header'] = array(
          t('Enabled'),
          'form_id',
          t('Recipe book'),
        );
        $form['botcha_form_protection']['botcha_form_id_overview']['botcha_forms'] = array();
        $forms = Botcha::getForms(TRUE);
        foreach ($forms as $botcha_form) {

          // Get individual admin form as a row of our overview table.
          $item_form = Botcha::getAdminForm('form_edit', array(), $botcha_form);

          // Remove submit button ...
          unset($item_form['submit']);

          // ... and botcha_operation field ...
          unset($item_form['botcha_operation']);

          // ... and also additional properties of some fields.
          unset($item_form['botcha_enabled']['#title']);
          unset($item_form['botcha_enabled']['#description']);

          /* @todo Remove it.
             foreach (element_children($item_form) as $column) {
               // Workaround for machine_name which is impossible to leave without
               // title and description, because it fills them with defaults.
               $item_form[$column]['#title'] = ' ';
               $item_form[$column]['#description'] = ' ';
             }
              *
              */
          unset($item_form['botcha_enabled']['#title']);
          unset($item_form['botcha_enabled']['#description']);
          $form['botcha_form_protection']['botcha_form_id_overview']['botcha_forms'][$botcha_form->id] = $item_form;

          /* @todo Remove it.
             $form['botcha_form_protection']['botcha_form_id_overview']['botcha_forms'][$botcha_form->id] = array();
             $form['botcha_form_protection']['botcha_form_id_overview']['botcha_forms'][$botcha_form->id]['form_id'] = array(
               '#markup' => $botcha_form->id,
             );
             // Recipe book, enabled for this form.
             $recipebook = $botcha_form->getRecipebook();
             $form['botcha_form_protection']['botcha_form_id_overview']['botcha_forms'][$botcha_form->id]['recipebook'] = array(
               // @todo Remove hardcode.
               // @todo Abstract it.
               //'#value' => ...,
               '#markup' => ($recipebook instanceof BotchaRecipebookNone || $recipebook->id == 'forbidden_forms')
                 ? $recipebook->id
                 : l($recipebook->id, Botcha::BOTCHA_ADMIN_PATH . "/recipebook/{$recipebook->id}"),
             );
             // Additional operations.
             $form['botcha_form_protection']['botcha_form_id_overview']['botcha_forms'][$botcha_form->id]['operations'] = array(
               '#markup' => ($recipebook->id == 'forbidden_forms')
                 ? ''
                 : implode(" | ", array(
                   l(t('Edit'), Botcha::BOTCHA_ADMIN_PATH . "/form/{$botcha_form->id}"),
                   l(t('Disable'), Botcha::BOTCHA_ADMIN_PATH . "/form/{$botcha_form->id}/delete"),
                 )),
             );
              *
              */
        }

        // Field for the BOTCHA administration mode.
        $form['botcha_form_protection']['botcha_administration_mode'] = array(
          '#type' => 'checkbox',
          '#title' => t('Add BOTCHA administration links to forms'),
          '#default_value' => variable_get('botcha_administration_mode', FALSE),
          '#description' => t('This option makes it easy to manage BOTCHA settings on forms. When enabled, users with the "%adminbotcha" permission will see a fieldset with BOTCHA administration links on all forms, except on administrative pages.', array(
            '%adminbotcha' => t('administer BOTCHA settings'),
          )),
        );

        // Field for the BOTCHAs on admin pages.
        $form['botcha_form_protection']['botcha_allow_on_admin_pages'] = array(
          '#type' => 'checkbox',
          '#title' => t('Allow BOTCHAs and BOTCHA administration links on administrative pages'),
          '#default_value' => variable_get('botcha_allow_on_admin_pages', FALSE),
          '#description' => t('This option makes it possible to add BOTCHAs to forms on administrative pages. BOTCHAs are disabled by default on administrative pages (which shouldn\'t be accessible to untrusted users normally) to avoid the related overhead. In some situations, e.g. in the case of demo sites, it can be usefull to allow BOTCHAs on administrative pages.'),
        );
        $form['buttons']['submit'] = array(
          '#type' => 'submit',
          '#value' => t('Save configuration'),
        );
        $form['#theme'] = 'system_settings_form';
        break;
    }
    return $form;
  }

  /**
   * Unified submit handler for admin forms.
   */
  public static function submitAdminForm($form_name, $form, &$form_state) {
    switch ($form_name) {
      case 'form_edit':
        $form_id = $form_state['values']['botcha_form_id'];
        $enabled = $form_state['values']['botcha_enabled'];
        $rbid = $form_state['values']['botcha_form_recipebook'];
        $operation = $form_state['values']['botcha_operation'];
        $add = $operation == 'add';
        Botcha::getForm($form_id, $add)
          ->setEnabled($enabled)
          ->setRecipebook($rbid)
          ->save();
        switch ($operation) {
          case 'edit':

            // Check if the form was really modified.
            // @todo Refactor in a more general manner.
            $edited = $form['botcha_enabled']['#default_value'] != $form['botcha_enabled']['#value'] || $form['botcha_form_recipebook']['#default_value'] != $form['botcha_form_recipebook']['#value'];
            if ($edited) {
              drupal_set_message(t('Saved BOTCHA form settings for %form_id.', array(
                '%form_id' => $form_id,
              )), 'status');
            }
            break;
          case 'add':
          default:

            // Check if the form was really modified.
            // @todo Refactor in a more general manner.
            $added = $form['botcha_form_id']['#default_value'] != $form['botcha_form_id']['#value'] || $form['botcha_form_recipebook']['#default_value'] != $form['botcha_form_id']['#value'];
            if ($added) {
              drupal_set_message(t('Added BOTCHA form %form_id.', array(
                '%form_id' => $form_id,
              )), 'status');
            }
            break;
        }
        $form_state['redirect'] = Botcha::BOTCHA_ADMIN_PATH . '/form';
        break;
      case 'form_list':
        $forms = $form['botcha_form_protection']['botcha_form_id_overview']['botcha_forms'];
        $form_ids = element_children($forms);
        foreach ($form_ids as $form_id) {

          // @todo Replace this workaround with more beautiful solution.
          $fake_form_state['values'] = array(
            'botcha_form_id' => $form_state['values']['botcha_form_id_overview']['botcha_forms'][$form_id]['botcha_form_id'],
            'botcha_enabled' => $form_state['values']['botcha_form_id_overview']['botcha_forms'][$form_id]['botcha_enabled'],
            'botcha_form_recipebook' => $form_state['values']['botcha_form_id_overview']['botcha_forms'][$form_id]['botcha_form_recipebook'],
            'botcha_operation' => 'edit',
          );
          Botcha::submitAdminForm('form_edit', $forms[$form_id], $fake_form_state);
        }

        // Do what system_settings_form() would do with regular variable fields
        variable_set('botcha_on_captcha_forms', !empty($form_state['values']['botcha_on_captcha_forms']) ? $form_state['values']['botcha_on_captcha_forms'] : FALSE);
        variable_set('botcha_administration_mode', $form_state['values']['botcha_administration_mode']);
        variable_set('botcha_allow_on_admin_pages', $form_state['values']['botcha_allow_on_admin_pages']);
        drupal_set_message(t('The BOTCHA settings were saved.'), 'status');
        break;
    }
  }

  /**
   * Get a list of available BOTCHA form objects.
   * @param boolean $reset
   * @return BotchaForm
   */
  public static function getForms($reset = FALSE) {
    $forms = array();
    $fs = array_keys(BotchaFormModel::getForms());
    foreach ($fs as $form_id) {
      $form_session =& $_SESSION[self::BOTCHA_SESSION_FORMS][$form_id];
      if (empty($form_session) || $reset) {
        Botcha::setForm(Botcha::getForm($form_id, FALSE));
      }
      $forms[$form_id] = unserialize($form_session);
    }
    return $forms;
  }

  /**
   * Gets a form from cache. If it does not exists in cache - gets from
   * database. If it does not exists there also - returns new form or
   * BotchaFormNone depending on input parameter.
   * @param string $form_id
   * @param boolean $create
   *   Determines whether we should initialize new recipe book or not.
   * @return BotchaForm
   */
  public static function getForm($form_id, $create = TRUE) {

    // We are using $_SESSION to store the state of the spam checking, because
    // HTTP is stateless by design.
    $form_session =& $_SESSION[self::BOTCHA_SESSION_FORMS][$form_id];
    if (empty($form_session) || $create) {
      $form = BotchaForm::getForm($form_id, $create);
      if (!$form instanceof BotchaFormNone) {

        // Set relationships for this concrete form.
        $rbs = BotchaModel::getFormsRecipebooks(array(
          'mode' => 'recipebook',
          'forms' => $form_id,
        ));
        foreach ($rbs as $rbid) {
          $form = $form
            ->setRecipebook($rbid);
        }
      }
      Botcha::setForm($form);
    }
    $form = unserialize($form_session);
    return $form;
  }

  /**
   * Sets form to the cache.
   * @param BotchaForm $form
   */
  public static function setForm($form) {
    $_SESSION[self::BOTCHA_SESSION_FORMS][$form->id] = serialize($form);
  }
  public static function unsetForm($form) {
    unset($_SESSION[self::BOTCHA_SESSION_FORMS][$form->id]);
  }

  /**
   * Get a list of all BOTCHA recipe book objects.
   * @param boolean $reset
   * @return BotchaRecipebookAbstract
   */
  public static function getRecipebooks($reset = FALSE) {
    $recipebooks = array();
    $rbs = array_keys(BotchaRecipebookModel::getRecipebooks());
    foreach ($rbs as $rbid) {
      $recipebook_session =& $_SESSION[self::BOTCHA_SESSION_RECIPEBOOKS][$rbid];
      if (empty($recipebook_session) || $reset) {
        Botcha::setRecipebook(Botcha::getRecipebook($rbid, FALSE));
      }
      $recipebooks[$rbid] = unserialize($recipebook_session);
    }
    return $recipebooks;
  }

  /**
   * Gets a recipe book from cache. If it does not exists in cache - gets from
   * database. If it does not exists there also - returns new recipe book or
   * BotchaRecipebookNone depending on input parameter.
   * @param string $id
   *   Machine name of the recipe book to look for.
   * @param boolean $create
   *   Determines whether we should initialize new recipe book or not.
   * @return BotchaRecipebookAbstract
   */
  public static function getRecipebook($id = 'default', $create = TRUE) {
    $recipebook_session =& $_SESSION[self::BOTCHA_SESSION_RECIPEBOOKS][$id];
    if (empty($recipebook_session) || $create) {
      $recipebook = BotchaRecipebook::getRecipebook($id, $create);
      if (!$recipebook instanceof BotchaRecipebookNone) {

        // Set relationships for this concrete recipe book.
        $fs = BotchaModel::getRecipebooksForms(array(
          'mode' => 'form',
          'recipebooks' => $id,
        ));
        foreach ($fs as $form_id) {
          $recipebook = $recipebook
            ->setForm($form_id);
        }

        // Get recipe book - recipe relationships.
        $rs = BotchaModel::getRecipebooksRecipes(array(
          'mode' => 'recipe',
          'recipebooks' => $id,
        ));
        foreach ($rs as $recipe_id) {
          $recipebook = $recipebook
            ->setRecipe($recipe_id);
        }
      }
      Botcha::setRecipebook($recipebook);
    }
    $recipebook = unserialize($recipebook_session);
    return $recipebook;
  }

  /**
   * Sets recipe book to the cache.
   * @param BotchaRecipebookAbstract $recipebook
   */
  public static function setRecipebook($recipebook) {
    $_SESSION[self::BOTCHA_SESSION_RECIPEBOOKS][$recipebook->id] = serialize($recipebook);
  }
  public static function unsetRecipebook($recipebook) {
    unset($_SESSION[self::BOTCHA_SESSION_RECIPEBOOKS][$recipebook->id]);
  }

  /**
   * Get a list of all BOTCHA recipes objects.
   * @param boolean $reset
   * @return array
   */
  public static function getRecipes($reset = FALSE) {
    $recipes = array();
    $rs = array_keys(BotchaRecipeModel::getRecipes());
    foreach ($rs as $recipe_id) {
      $recipe_session =& $_SESSION[self::BOTCHA_SESSION_RECIPES][$recipe_id];
      if (empty($recipe_session) || $reset) {
        Botcha::setRecipe(BotchaRecipe::getRecipe($recipe_id));
      }
      $recipes[$recipe_id] = unserialize($recipe_session);
    }
    return $recipes;
  }

  /**
   * Gets a recipe from cache. If it does not exists in cache - gets from
   * database. If it does not exists there also - returns NULL.
   * @param string $id
   *   Machine name of the recipe to look for.
   * @param boolean $create
   *   Determines whether we should initialize new recipe book or not.
   * @return BotchaRecipe
   */
  public static function getRecipe($id, $create = TRUE) {
    $recipe_session =& $_SESSION[self::BOTCHA_SESSION_RECIPES][$id];
    if (empty($recipe_session)) {
      $recipe = BotchaRecipe::getRecipe($id, $create);

      // @todo ?Do we need it?

      //if (!($recipe1 instanceof BotchaRecipeNone)) {

      // Set relationships for this concrete recipe.
      $rbs = BotchaModel::getRecipesRecipebooks(array(
        'mode' => 'recipebook',
        'recipes' => $id,
      ));
      foreach ($rbs as $rbid) {
        $recipe = $recipe
          ->setRecipebook($rbid);
      }

      //}
      Botcha::setRecipe($recipe);
    }
    $recipe = unserialize($recipe_session);
    return $recipe;
  }

  /**
   * Sets recipe to the $_SESSION.
   * @param BotchaRecipe $recipe
   */
  public static function setRecipe($recipe) {
    $_SESSION[self::BOTCHA_SESSION_RECIPES][$recipe->id] = serialize($recipe);
  }
  public static function unsetRecipe($recipe) {
    unset($_SESSION[self::BOTCHA_SESSION_RECIPES][$recipe->id]);
  }
  public static function clean() {
    unset($_SESSION[self::BOTCHA_SESSION_FORMS]);
    unset($_SESSION[self::BOTCHA_SESSION_RECIPEBOOKS]);
    unset($_SESSION[self::BOTCHA_SESSION_RECIPES]);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
Botcha::BOTCHA_ADMIN_PATH constant
Botcha::BOTCHA_SESSION_FORMS constant
Botcha::BOTCHA_SESSION_RECIPEBOOKS constant
Botcha::BOTCHA_SESSION_RECIPES constant
Botcha::clean public static function
Botcha::getAdminForm public static function Get admin form.
Botcha::getForm public static function Gets a form from cache. If it does not exists in cache - gets from database. If it does not exists there also - returns new form or BotchaFormNone depending on input parameter.
Botcha::getForms public static function Get a list of available BOTCHA form objects.
Botcha::getRecipe public static function Gets a recipe from cache. If it does not exists in cache - gets from database. If it does not exists there also - returns NULL.
Botcha::getRecipebook public static function Gets a recipe book from cache. If it does not exists in cache - gets from database. If it does not exists there also - returns new recipe book or BotchaRecipebookNone depending on input parameter.
Botcha::getRecipebooks public static function Get a list of all BOTCHA recipe book objects.
Botcha::getRecipes public static function Get a list of all BOTCHA recipes objects.
Botcha::setForm public static function Sets form to the cache.
Botcha::setRecipe public static function Sets recipe to the $_SESSION.
Botcha::setRecipebook public static function Sets recipe book to the cache.
Botcha::submitAdminForm public static function Unified submit handler for admin forms.
Botcha::unsetForm public static function
Botcha::unsetRecipe public static function
Botcha::unsetRecipebook public static function
Botcha::__clone private function
Botcha::__construct private function
Botcha::__wakeup private function