You are here

botcha.admin.inc in BOTCHA Spam Prevention 7.2

Same filename and directory in other branches
  1. 6.2 botcha.admin.inc

Implementation of botcha administration forms.

File

botcha.admin.inc
View source
<?php

// @todo Migrate all necessary functions not to have this including.
module_load_include('inc', 'botcha', 'botcha');

/**
 * @file
 * Implementation of botcha administration forms.
 */

/**
 * Generate a random secret key.
 */
function botcha_generate_secret_key() {
  return md5(uniqid(mt_rand(), TRUE));
}

/**
 * Module settings form.
 */
function botcha_admin_settings($form, &$form_state) {

  // We can't use system_settings_form() here because it will put all extra stuff into variables, that we want to avoid.
  $form = array();
  $form['botcha_secret'] = array(
    '#type' => 'textfield',
    '#title' => t('Secret key'),
    '#default_value' => variable_get('botcha_secret', botcha_generate_secret_key()),
    '#description' => t('It is recommended to enter some random text into the secret key. This setting makes your site\'s BOTCHA challenges unique and harder to break.') . '<br />' . t('If you leave this field empty and save configuration, a new random key will be generated for you.'),
  );

  // BOTCHA Statistics & Logging
  $form['botcha_statistics'] = array(
    '#type' => 'fieldset',
    '#title' => t('Statistics & logging'),
    '#description' => t('BOTCHA collects statistics of form submissions and it can report different events into the system log.'),
  );
  $dblog_link = l(t('log'), 'admin/reports/dblog');
  $form['botcha_statistics']['botcha_loglevel'] = array(
    '#type' => 'select',
    '#title' => t('Log level'),
    '#default_value' => variable_get('botcha_loglevel', 2),
    '#options' => array(
      0 => t('0: no log'),
      1 => t('1: blocked/bad submissions only'),
      2 => t('2: ... and why blocked'),
      3 => t('3: ... and good submissions'),
      4 => t('4: ... and protected forms'),
      5 => t('5: ... and extra submission details'),
      6 => t('6: ... and misc development items'),
    ),
    '#description' => t('Select what information to report into the !log.' . ' Please note!: Using BOTCHA logging setting could cause at high' . ' levels putting vulnerable data into logs. We have some basic' . ' escaping (e.g., for password field) - but any other data could' . ' be found in raw format. Please be careful with logging level' . ' setting!', array(
      '!log' => $dblog_link,
    )),
  );

  // Button for resetting the BOTCHA statistics.
  $form['botcha_statistics']['botcha_statistics_group'] = array(
    '#type' => 'item',
    '#title' => t('BOTCHA statistics'),
    '#description' => t('Reset all accumulated statistics of form submissions.'),
  );

  // Handle the button for resetting the BOTCHA statistics.
  // This is done here instead of in a submit handler because the button is
  // not a submitting button.
  $form['botcha_statistics']['botcha_statistics_group']['botcha_statistics_reset'] = array(
    '#type' => 'button',
    '#value' => t('Reset BOTCHA statistics'),
    '#submit' => array(
      'botcha_statistics_reset',
    ),
    // Pull it down.
    '#weight' => 100,
  );
  if (isset($form_state['input']['op']) && $form_state['input']['op'] == $form['botcha_statistics']['botcha_statistics_group']['botcha_statistics_reset']['#value']) {
    variable_set('botcha_form_passed_counter', 0);
    variable_set('botcha_form_blocked_counter', 0);
    drupal_set_message(t('BOTCHA statistics have been reset.'));
  }

  // Show statistic counters.
  $block_cnt = variable_get('botcha_form_blocked_counter', 0);
  $build_cnt = variable_get('botcha_form_passed_counter', 0) + $block_cnt;
  $form['botcha_statistics']['botcha_statistics_group']['botcha_statistics'] = array(
    '#type' => 'item',
    '#markup' => format_plural($block_cnt, 'Already 1 blocked form submission.', 'Already @count blocked form submissions.') . ($build_cnt > 0 ? ' ' . t('(!percent% of total !build_cnt processed)', array(
      '!percent' => sprintf("%0.3f", 100 * $block_cnt / $build_cnt),
      '!build_cnt' => $build_cnt,
    )) : ''),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save configuration'),
  );
  $form['#theme'] = 'system_settings_form';
  return $form;
}

/**
 * Submission function for botcha_admin_settings form.
 */
function botcha_admin_settings_submit($form, &$form_state) {

  // Generate the secret key.
  // @todo botcha_admin_settings_submit Move secret key generation to validate phase.
  if (empty($form_state['values']['botcha_secret'])) {

    // Generate unique secret for this site
    $secret = botcha_generate_secret_key();
    $form_state['values']['botcha_secret'] = $secret;
    drupal_set_message(t('New BOTCHA secret key have been generated.'));
  }

  // Do what system_settings_form() would do with regular variable fields
  variable_set('botcha_secret', $form_state['values']['botcha_secret']);
  variable_set('botcha_loglevel', $form_state['values']['botcha_loglevel']);
  drupal_set_message(t('The BOTCHA settings were saved.'), 'status');
}

/**
 * Edit existent or add a new recipe book.
 * @param array $form
 *  Form API form array.
 * @param array $form_state
 *  Form API form state array.
 * @param BotchaRecipebook $recipebook
 *  Recipe book object.
 */
function botcha_recipebook_form($form, &$form_state, $recipebook = NULL) {

  // Determine default values depending on whether we add or edit recipe book.
  // Form a list of recipes.
  $options_recipes = array();
  foreach (Botcha::getRecipes() as $recipe) {
    $options_recipes[$recipe->id] = $recipe->title;
  }
  if (empty($recipebook)) {
    $disabled_id = FALSE;
    $default_id = '';
    $default_title = '';
    $default_description = '';
    $default_recipes = array();
    $button = t('Add');
  }
  else {
    $disabled_id = TRUE;
    $default_id = $recipebook->id;
    $default_title = $recipebook->title;
    $default_description = $recipebook->description;
    $default_recipes = array_keys($recipebook
      ->getRecipes());
    $button = t('Save');
  }
  $form['id'] = array(
    '#type' => 'machine_name',
    '#default_value' => $default_id,
    '#disabled' => $disabled_id,
    '#maxlength' => 128,
    '#machine_name' => array(
      'exists' => 'botcha_recipebook_exists',
    ),
  );
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Title'),
    '#description' => t('A title for this recipe book. You can always change this name later.'),
    '#default_value' => $default_title,
    '#required' => TRUE,
    '#maxlength' => 128,
  );
  $form['description'] = array(
    '#type' => 'textarea',
    '#rows' => 5,
    '#title' => t('Description'),
    '#description' => t('A description of the recipe book.'),
    '#default_value' => $default_description,
  );
  $form['recipes'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Recipes'),
    '#description' => t('Choose what recipes are included in recipe book.'),
    '#options' => $options_recipes,
    '#default_value' => $default_recipes,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => $button,
  );
  return $form;
}

/**
 * Submit handler for botcha_recipebook_form.
 * @param $form
 *  Form API form array.
 * @param $form_state
 *  Form API form state array.
 */
function botcha_recipebook_form_submit($form, &$form_state) {
  $values = $form_state['values'];
  $recipebook = Botcha::getRecipebook($values['id'])
    ->setTitle($values['title'])
    ->setDescription($values['description']);

  /*
    foreach (array_filter($values['recipes']) as $recipe_id) {
   $recipebook->setRecipe($recipe_id);
    }
  *
  */
  foreach ($values['recipes'] as $recipe_id => $value) {
    if ($value) {
      $recipebook
        ->setRecipe($recipe_id);
    }
    else {
      $recipebook
        ->unsetRecipe($recipe_id);
    }
  }
  $recipebook
    ->save();
  $form_state['redirect'] = Botcha::BOTCHA_ADMIN_PATH . '/recipebook';
  drupal_set_message(t('Settings for recipe book "%recipebook" are successfully saved.', array(
    '%recipebook' => $recipebook->id,
  )), 'status');
}

/**
 * Delete configuration form.
 */
function botcha_recipebook_delete_form($form, &$form_state, $recipebook) {
  $form['#recipebook'] = $recipebook;
  return confirm_form($form, t('Would you really like to delete the recipe book @recipebook?', array(
    '@recipebook' => $recipebook->title,
  )), Botcha::BOTCHA_ADMIN_PATH . '/recipebook', t('This action cannot be undone.'), t('Delete'));
}

/**
 * Submit handler for botcha_recipebook_delete_form().
 */
function botcha_recipebook_delete_form_submit($form, &$form_state) {
  $form_state['redirect'] = Botcha::BOTCHA_ADMIN_PATH . '/recipebook';

  // Remove recipe book.
  $form['#recipebook']
    ->delete();
}
function botcha_recipebook_exists($value) {
  return !Botcha::getRecipebook($value, FALSE) instanceof BotchaRecipebookNone;
}

/**
 * Edit existent or add BOTCHA protection to another form.
 * @param array $form
 *  Form API form array.
 * @param array $form_state
 *  Form API form state array.
 * @param BotchaForm $botcha_form
 *  Botcha form object.
 */
function botcha_form_form($form, $form_state, $botcha_form = NULL) {
  return Botcha::getAdminForm('form_edit', $form_state, $botcha_form);
}
function botcha_form_exists($value) {
  return !Botcha::getForm($value, FALSE) instanceof BotchaFormNone;
}

/**
 * Submit handler for botcha_form_form.
 */
function botcha_form_form_submit($form, &$form_state) {
  Botcha::submitAdminForm('form_edit', $form, $form_state);
}

/**
 * Confirm dialog for deleting a BOTCHA form completely.
 */
function botcha_form_delete_form($form, $form_state, $botcha_form = NULL) {
  $form = array();
  $form['botcha_form_id'] = array(
    '#type' => 'value',
    '#value' => $botcha_form->id,
  );
  $message = t('Are you sure you want to delete the BOTCHA protection for form_id %form_id?', array(
    '%form_id' => $botcha_form->id,
  ));
  return confirm_form($form, $message, Botcha::BOTCHA_ADMIN_PATH, NULL, t('Delete'));
}

/**
 * Submission handler of BOTCHA form deleting.
 */
function botcha_form_delete_form_submit($form, &$form_state) {
  $form_id = $form_state['values']['botcha_form_id'];
  Botcha::getForm($form_id, FALSE)
    ->delete();
  drupal_set_message(t('Deleted BOTCHA protection for form %form_id.', array(
    '%form_id' => $form_id,
  )));
  $form_state['redirect'] = Botcha::BOTCHA_ADMIN_PATH . '/form';
}

/**
 * Callback for "Forms" admin page.
 * Configuration of which forms to protect, with what recipe.
 */
function botcha_forms_form() {
  return Botcha::getAdminForm('form_list');
}

/**
 * Submission handler for botcha_forms_form form.
 */
function botcha_forms_form_submit($form, &$form_state) {
  Botcha::submitAdminForm('form_list', $form, $form_state);
}

/**
 * Callback for "Recipe books" admin page.
 * @todo ?Is it form really? Perhaps table?
 */
function botcha_recipebooks_form() {
  $form['#header'] = array(
    t('Title'),
    t('Description'),
    t('Operations'),
  );

  // Get all recipe books from database.
  $recipebooks = Botcha::getRecipebooks();

  // Protect default recipebook from being deleted.
  foreach ($recipebooks as $recipebook) {
    $form['recipebooks'][$recipebook->id]['title']['#markup'] = $recipebook->title;
    $form['recipebooks'][$recipebook->id]['description']['#markup'] = $recipebook->description;
    $form['recipebooks'][$recipebook->id]['operations']['#markup'] = in_array($recipebook->id, array(
      'forbidden_forms',
    )) ? '' : l(t('Edit'), Botcha::BOTCHA_ADMIN_PATH . "/recipebook/{$recipebook->id}") . (in_array($recipebook->id, array(
      'default',
      'forbidden_forms',
    )) ? '' : ' | ' . l(t('Delete'), Botcha::BOTCHA_ADMIN_PATH . "/recipebook/{$recipebook->id}/delete"));
  }
  return $form;
}
function botcha_recipes_form() {

  // @todo Implement Recipe UI.
  // @see https://drupal.org/node/1815080
  $form = array();
  $form['stub'] = array(
    '#markup' => t('This functionality is currently in development. See <a href="@issue_link">related issue</a>. Please consider participating in <a href="@patchranger_link">patch crowd funding of this issue</a>. Read more about patch crowd funding on <a href="@botcha_project_link">the BOTCHA project page</a>.', array(
      '@issue_link' => url('http://drupal.org/node/1815080'),
      '@patchranger_link' => url('http://www.patchranger.com/?do_nid=1815080'),
      '@botcha_project_link' => url('http://drupal.org/project/botcha#how-much-does-it-cost'),
    )),
  );
  return $form;
}

/**
 * Custom theme function for a table of (form_id -> BOTCHA type) settings
 */
function theme_botcha_forms_form_botcha_forms($variables) {
  $form = $variables['form'];

  // Prepare header before pass to theme.
  $header = $form['#header'];
  $rows = array();

  // Existing BOTCHA points.
  foreach (element_children($form['botcha_forms']) as $id) {
    $row = array();
    foreach (element_children($form['botcha_forms'][$id]) as $col) {
      $row[$col] = drupal_render($form['botcha_forms'][$id][$col]);
    }
    $rows[$id] = $row;
  }
  $output = theme('table', array(
    'header' => $header,
    'rows' => $rows,
  ));
  return $output;
}

/**
 * Theme botcha_recipebooks_form().
 */
function theme_botcha_recipebooks_form($variables) {
  $form = $variables['form'];

  // Iterate through all recipebooks and build a table.
  $rows = array();

  //foreach (array('enabled', 'disabled') as $type) {

  //  if (isset($form[$type])) {
  foreach (element_children($form['recipebooks']) as $id) {
    $row = array();
    foreach (element_children($form['recipebooks'][$id]) as $col) {
      $row[$col] = array(
        'data' => drupal_render($form['recipebooks'][$id][$col]),
      );
    }
    $rows[] = array(
      'data' => $row,
    );
  }

  //  }

  //}
  $output = theme('table', array(
    'header' => $form['#header'],
    'rows' => $rows,
    'empty' => t('No recipebooks available.'),
  ));
  if (!empty($rows)) {
    $output .= drupal_render_children($form);
  }
  return $output;
}

//END

Functions

Namesort descending Description
botcha_admin_settings Module settings form.
botcha_admin_settings_submit Submission function for botcha_admin_settings form.
botcha_forms_form Callback for "Forms" admin page. Configuration of which forms to protect, with what recipe.
botcha_forms_form_submit Submission handler for botcha_forms_form form.
botcha_form_delete_form Confirm dialog for deleting a BOTCHA form completely.
botcha_form_delete_form_submit Submission handler of BOTCHA form deleting.
botcha_form_exists
botcha_form_form Edit existent or add BOTCHA protection to another form.
botcha_form_form_submit Submit handler for botcha_form_form.
botcha_generate_secret_key Generate a random secret key.
botcha_recipebooks_form Callback for "Recipe books" admin page. @todo ?Is it form really? Perhaps table?
botcha_recipebook_delete_form Delete configuration form.
botcha_recipebook_delete_form_submit Submit handler for botcha_recipebook_delete_form().
botcha_recipebook_exists
botcha_recipebook_form Edit existent or add a new recipe book.
botcha_recipebook_form_submit Submit handler for botcha_recipebook_form.
botcha_recipes_form
theme_botcha_forms_form_botcha_forms Custom theme function for a table of (form_id -> BOTCHA type) settings
theme_botcha_recipebooks_form Theme botcha_recipebooks_form().