You are here

botcha.admin.inc in BOTCHA Spam Prevention 6.2

Same filename and directory in other branches
  1. 7.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));
}
function botcha_admin_settings(&$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', BOTCHA_LOGLEVEL),
    '#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.', 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['post']['op']) && $form_state['post']['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',
    '#value' => 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 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');
}

/**
 * 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);
}

/**
 * Edit existent or add BOTCHA protection to another form.
 * @param array $form_state
 *  Form API form state array.
 * @param BotchaForm $botcha_form
 *  Botcha form object.
 */
function botcha_form_form(&$form_state, $botcha_form = NULL) {
  return Botcha::getAdminForm('form_edit', $form_state, $botcha_form);
}
function botcha_form_id_validate($element, &$form_state) {
  $value = $element['#value'];

  // @todo ?Is it correct way to check if the form is protected?
  if (!Botcha::getForm($value, FALSE)
    ->getRecipebook() instanceof BotchaRecipebookNone) {
    form_set_error('botcha_form_id', t('Form %form_id is already protected by BOTCHA', array(
      '%form_id' => $value,
    )));
  }
}

/**
 * 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_state, $botcha_form) {
  $form['#botcha_form'] = $botcha_form;
  return confirm_form($form, t('Are you sure you want to delete the BOTCHA protection for form_id %form_id?', array(
    '%form_id' => $botcha_form->id,
  )), Botcha::BOTCHA_ADMIN_PATH . '/form', NULL, t('Delete'));
}

/**
 * Submission handler of BOTCHA form deleting.
 */
function botcha_form_delete_form_submit($form, &$form_state) {
  $botcha_form = $form['#botcha_form'];
  drupal_set_message(t('Deleted BOTCHA protection for form %form_id.', array(
    '%form_id' => $botcha_form->id,
  )));

  // Remove BOTCHA protection for form.
  $botcha_form
    ->delete();
  $form_state['redirect'] = Botcha::BOTCHA_ADMIN_PATH . '/form';
}

/**
 * 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) {

    /* @todo Abstarct it.
       $form['recipebooks'][$recipebook->id]['title']['#markup'] = $recipebook->title;
       $form['recipebooks'][$recipebook->id]['description']['#markup'] = $recipebook->description;
       $form['recipebooks'][$recipebook->id]['operations']['#markup'] = l(t('Edit'), Botcha::BOTCHA_ADMIN_PATH . '/recipebook/' . $recipebook->id)
        *
        */
    $form['recipebooks'][$recipebook->id]['title']['#value'] = $recipebook->title;
    $form['recipebooks'][$recipebook->id]['description']['#value'] = $recipebook->description;
    $form['recipebooks'][$recipebook->id]['operations']['#value'] = 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;
}

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

    // Redirect in case we are trying to edit unexisting item.
    drupal_goto(Botcha::BOTCHA_ADMIN_PATH . '/recipebook/add', array(
      'query' => array(
        'botcha_rbid' => $recipebook->id,
      ),
    ));
  }

  // Determine default values depending on whether we add or edit recipe book.
  if ($edit = !empty($recipebook)) {
    $default_id = $recipebook->id;
    $default_title = $recipebook->title;
    $default_description = $recipebook->description;
    $default_recipes = array_keys($recipebook
      ->getRecipes());

    // @todo Abstract it.
    $validate = array();
    $button = t('Save');
  }
  else {

    // @todo Unused yet. Do we need it?
    $default_id = !empty($_GET['botcha_rbid']) ? $_GET['botcha_rbid'] : NULL;
    $default_title = '';
    $default_description = '';
    $default_recipes = array();

    // @todo Abstract it.
    $validate = array(
      'botcha_form_id_validate',
    );
    $button = t('Add');
  }
  $form['id'] = array(
    '#type' => 'textfield',
    '#title' => t('Id'),
    '#description' => t('The unique identifier of the recipe book.'),
    // @todo Abstract it.

    //'#default_value' => $default_id,

    //'#value' => $default_id,
    '#required' => TRUE,
    '#disabled' => $edit,
    '#maxlength' => 128,
    '#element_validate' => $validate,
  );

  // @todo Abstract it.
  if ($edit) {
    $form['id']['#value'] = $default_id;
  }
  else {
    $form['id']['#default_value'] = $default_id;
  }
  $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 a list of recipes.
  $form['recipes'] = array(
    '#type' => 'fieldset',
    '#title' => t('Recipes'),
    '#description' => t('Choose what recipes are included in recipe book.'),
    '#tree' => TRUE,
  );
  foreach (Botcha::getRecipes() as $recipe) {
    $form['recipes'][$recipe->id] = array(
      '#type' => 'checkbox',
      '#title' => $recipe
        ->getTitle(),
      '#description' => $recipe
        ->getDescription(),
      '#default_value' => in_array($recipe->id, $default_recipes),
    );
  }
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => $button,
  );
  return $form;
}
function botcha_recipebook_id_validate($element, $form_state) {
  $value = $element['#value'];
  if (!Botcha::getRecipebook($value, FALSE) instanceof BotchaRecipebookNone) {
    form_set_error('id', t('Recipe book %rbid already exists', array(
      '%rbid' => $value,
    )));
  }
}

/**
 * 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 = $recipebook
        ->setRecipe($recipe_id);
    }
    else {
      $recipebook = $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_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', NULL, t('Delete'));
}

/**
 * Submit handler for botcha_recipebook_delete_form().
 */
function botcha_recipebook_delete_form_submit($form, &$form_state) {
  $recipebook = $form['#recipebook'];
  drupal_set_message(t('Recipe book %rbid successfully deleted.', array(
    '%rbid' => $recipebook->id,
  )));

  // Remove recipe book.
  $recipebook
    ->delete();
  $form_state['redirect'] = Botcha::BOTCHA_ADMIN_PATH . '/recipebook';
}
function botcha_recipes_form() {

  // @todo Implement Recipe UI.
  // @see https://drupal.org/node/1815080
  $form = array();
  $form['stub'] = array(
    '#value' => 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;
}

/**
 * Theme botcha_recipebooks_form().
 */
function theme_botcha_recipebooks_form($form) {

  // @todo Abstract it.
  // "Add recipebook" link.
  $output = l(t('Add recipe book'), Botcha::BOTCHA_ADMIN_PATH . '/recipebook/add');

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

  // Iterate through all recipebooks and build a table.
  $rows = array();
  foreach (element_children($form['recipebooks']) as $id) {
    $row = array();
    foreach (element_children($form['recipebooks'][$id]) as $col) {
      $row[$col] = drupal_render($form['recipebooks'][$id][$col]);
    }
    $rows[$id] = $row;
  }
  $output .= theme('table', $header, $rows);
  return $output;
}

/**
 * Custom theme function for a table of (form_id -> BOTCHA type) settings
 */
function theme_botcha_forms_form_botcha_forms($form) {

  // @todo Abstract it.
  // "Add BOTCHA protection to the form" link.
  $output = l(t('Add BOTCHA protection to the form'), Botcha::BOTCHA_ADMIN_PATH . '/form/add');

  // 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', $header, $rows);
  return $output;
}

//END

Functions

Namesort descending Description
botcha_admin_settings
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_form Edit existent or add BOTCHA protection to another form.
botcha_form_form_submit Submit handler for botcha_form_form.
botcha_form_id_validate
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_form Edit existent or add a new recipe book.
botcha_recipebook_form_submit Submit handler for botcha_recipebook_form.
botcha_recipebook_id_validate
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().