botcha.controller.inc in BOTCHA Spam Prevention 6.2
Same filename and directory in other branches
Contains Botcha class.
Controller layer of the BOTCHA application.
File
controller/botcha.controller.incView source
<?php
/**
* @file
* Contains Botcha class.
*
* Controller layer of the BOTCHA application.
*/
// @todo Migrate all necessary functions not to have this including.
module_load_include('inc', 'botcha', 'botcha');
module_load_include('inc', 'botcha', 'model/botcha.model');
module_load_include('inc', 'botcha', 'controller/botcha_form.controller');
module_load_include('inc', 'botcha', 'controller/botcha_recipe.controller');
module_load_include('inc', 'botcha', 'controller/botcha_recipebook.controller');
/**
* Singleton realization of botcha application.
*/
class Botcha {
const BOTCHA_ADMIN_PATH = 'admin/user/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 */
}
public static function getAdminForm($form_name) {
$form = array();
switch ($form_name) {
case 'form_edit':
list($form_name, $form_state, $botcha_form) = func_get_args();
if ($botcha_form instanceof BotchaFormNone) {
// Redirect in case we are trying to edit unexisting item.
drupal_goto(Botcha::BOTCHA_ADMIN_PATH . '/form/add', array(
'query' => array(
'botcha_form_id' => $botcha_form->id,
),
));
}
// Determine default values depending on whether we add or edit form.
if ($edit = !empty($botcha_form)) {
$operation = 'edit';
$isEnabled = $botcha_form
->isEnabled();
$recipebook = $botcha_form
->getRecipebook();
$botcha_form_id_default = check_plain($botcha_form->id);
// @todo Abstract it.
$validate = array();
$button = t('Save');
}
else {
$operation = 'add';
$isEnabled = TRUE;
// 'default' will be selected.
$recipebook = Botcha::getRecipebook();
// @todo Abstract it.
//$query_params = drupal_get_query_parameters();
$botcha_form_id_default = !empty($_GET['botcha_form_id']) ? $_GET['botcha_form_id'] : NULL;
// @todo Abstract it.
$validate = array(
'botcha_form_id_validate',
);
$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' => 'textfield',
'#title' => t('Form ID'),
'#description' => t('The Drupal form_id of the form to add the BOTCHA protection to.'),
// @todo Abstract it.
//'#default_value' => $botcha_form_id_default,
//'#value' => $botcha_form_id_default,
'#required' => TRUE,
'#disabled' => $edit,
'#maxlength' => 128,
'#element_validate' => $validate,
);
// @todo Abstract it.
if ($edit) {
$form['botcha_form_id']['#value'] = $botcha_form_id_default;
}
else {
$form['botcha_form_id']['#default_value'] = $botcha_form_id_default;
}
$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,
'#disabled' => !$isEnabled,
);
$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(
// @todo Abstract it.
//'@captcha' => url('admin/config/people/captcha'),
'@captcha' => url('admin/user/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']);
$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]['form_id'] = array(
// @todo Abstract it.
//'#markup' => $botcha_form->id,
'#value' => $botcha_form->id,
);
// Recipe book, enabled for this form.
$recipebook = $botcha_form->getRecipebook();
// @todo Remove hardcode.
$value = ($recipebook instanceof BotchaRecipebookNone || $recipebook->id == 'forbidden_forms')
? $recipebook->id
: l($recipebook->id, Botcha::BOTCHA_ADMIN_PATH . "/recipebook/{$recipebook->id}");
$form['botcha_form_protection']['botcha_form_id_overview']['botcha_forms'][$botcha_form->id]['recipebook'] = array(
// @todo Abstract it.
//'#markup' => $botcha_form->id,
'#value' => $value,
);
// Additional operations.
$form['botcha_form_protection']['botcha_form_id_overview']['botcha_forms'][$botcha_form->id]['operations'] = array(
// @todo Abstract it.
//'#markup' => implode(" | ", array(
'#value' => ($recipebook->id == 'forbidden_forms')
? ''
: implode(" | ", array(
l(t('Edit'), Botcha::BOTCHA_ADMIN_PATH . "/form/{$botcha_form->id}"),
l(t('Delete'), 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['submit'] = array(
'#type' => 'submit',
'#value' => t('Save configuration'),
);
$form['#theme'] = 'system_settings_form';
break;
}
return $form;
}
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 = array_keys(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 $_SESSION.
* @param BotchaForm $form
*/
public static function setForm($form) {
//self::$forms[$form->id] = $form;
$_SESSION[self::BOTCHA_SESSION_FORMS][$form->id] = serialize($form);
}
public static function unsetForm($form) {
//unset(self::$forms[$form->id]);
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 = array_keys(BotchaModel::getRecipebooksForms(array(
'mode' => 'form',
'recipebooks' => $id,
)));
foreach ($fs as $form_id) {
$recipebook = $recipebook
->setForm($form_id);
}
// Get recipe book - recipe relationships.
$rs = array_keys(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 $_SESSION.
* @param BotchaRecipebookAbstract $recipebook
*/
public static function setRecipebook($recipebook) {
//self::$recipebooks[$recipebook->id] = $recipebook;
$_SESSION[self::BOTCHA_SESSION_RECIPEBOOKS][$recipebook->id] = serialize($recipebook);
}
public static function unsetRecipebook($recipebook) {
//unset(self::$recipebooks[$recipebook->id]);
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 = array_keys(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]);
}
}