diff --git a/botcha.admin.inc b/botcha.admin.inc
index 44f0aba..8b63c4b 100644
--- a/botcha.admin.inc
+++ b/botcha.admin.inc
@@ -31,7 +31,7 @@ function _botcha_admin_settings(&$form_state) {
   $form['botcha_statistics']['botcha_loglevel'] = array(
     '#type' => 'select',
     '#title' => t('Log level'),
-    '#default_value' => variable_get('botcha_loglevel', 1),
+    '#default_value' => variable_get('botcha_loglevel', BOTCHA_LOGLEVEL),
     '#options' => array(
       0 => t('0: no log'),
       1 => t('1: blocked/bad submissions only'),
@@ -82,22 +82,26 @@ function _botcha_admin_settings(&$form_state) {
 /**
  * Custom theme function for a table of (form_id -> BOTCHA type) settings
  */
-function theme_botcha_forms_form_botcha_forms($variables) {
-  $form = $variables['form'];
-  $header = array('form_id', t('Operations'));
+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'), 'admin/user/botcha/form/add');
+  // Prepare header before pass to theme.
+  $header = $form['#header'];
   $rows = array();
   // Existing BOTCHA points.
-  foreach (element_children($form['botcha_forms']) as $key) {
+  foreach (element_children($form['botcha_forms']) as $id) {
     $row = array();
-    $row[] = drupal_render($form['botcha_forms'][$key]['form_id']);
-    $row[] = drupal_render($form['botcha_forms'][$key]['operations']);
-    $rows[] = $row;
+    foreach (element_children($form['botcha_forms'][$id]) as $col) {
+      $row[] = drupal_render($form['botcha_forms'][$id][$col]);
+    }
+    $rows[$id] = $row;
   }
-  $output = theme('table', array('header' => $header, 'rows' => $rows));
+  $output .= theme('table', $header, $rows);
   return $output;
 }
 
-function botcha_admin_settings($form, &$form_state) {
+function botcha_admin_settings(&$form_state) {
 //  $form = system_settings_form(_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 = _botcha_admin_settings($form_state);
@@ -142,45 +146,60 @@ function botcha_admin_settings_submit($form, &$form_state) {
 
 /**
  * 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) {
+function botcha_recipebook_form(&$form_state, $recipebook = NULL) {
+  if ($recipebook instanceof BotchaRecipebookNone) {
+    // Redirect in case we are trying to edit unexisting item.
+    drupal_goto('admin/user/botcha/recipebook/add', array('query' => array('botcha_rbid' => $recipebook->id)));
+  }
   // 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 ($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');
   }
-  if (empty($recipebook)) {
-    $disabled_id = FALSE;
-    $default_id = '';
+  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');
   }
-  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 a list of recipes.
+  $options_recipes = array();
+  foreach (Botcha::getRecipes() as $recipe) {
+    $options_recipes[$recipe->id] = $recipe->title;
   }
   $form['id'] = array(
-    '#type' => 'machine_name',
-    '#default_value' => $default_id,
-    '#disabled' => $disabled_id,
+    '#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,
-    '#machine_name' => array(
-      'exists' => 'botcha_recipebook_exists',
-    ),
+    '#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'),
@@ -237,20 +256,24 @@ function botcha_recipebook_form_submit($form, &$form_state) {
     }
   }
   $recipebook->save();
-  $form_state['redirect'] = 'admin/config/people/botcha/recipebook';
+  // @todo Abstract it.
+  //$form_state['redirect'] = 'admin/config/people/botcha/recipebook';
+  $form_state['redirect'] = 'admin/user/botcha/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) {
+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)),
-    'admin/config/people/botcha/recipebook',
-    t('This action cannot be undone.'),
+    // @todo Abstract it.
+    //'admin/config/people/botcha/recipebook',
+    'admin/user/botcha/recipebook',
+    NULL,
     t('Delete')
   );
 }
@@ -259,44 +282,52 @@ function botcha_recipebook_delete_form($form, &$form_state, $recipebook) {
  * Submit handler for botcha_recipebook_delete_form().
  */
 function botcha_recipebook_delete_form_submit($form, &$form_state) {
-  $form_state['redirect'] = 'admin/config/people/botcha/recipebook';
+  $recipebook = $form['#recipebook'];
+  drupal_set_message(t('Recipe book %rbid successfully deleted.', array('%rbid' => $recipebook->id)));
   // Remove recipe book.
-  $form['#recipebook']->delete();
+  $recipebook->delete();
+  // @todo Abstract it.
+  //$form_state['redirect'] = 'admin/config/people/botcha/recipebook';
+  $form_state['redirect'] = 'admin/user/botcha/recipebook';
 }
 
-function botcha_recipebook_exists($value) {
-  return !(Botcha::getRecipebook($value, FALSE) instanceof BotchaRecipebookNone);
+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)));
+  }
 }
 
 /**
  * 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) {
+function botcha_form_form(&$form_state, $botcha_form = NULL) {
   $form = array();
+  if ($botcha_form instanceof BotchaFormNone) {
+    // Redirect in case we are trying to edit unexisting item.
+    drupal_goto('admin/user/botcha/form/add', array('query' => array('botcha_form_id' => $botcha_form->id)));
+  }
   // Determine default values depending on whether we add or edit form.
-  $edit = !empty($botcha_form);
-  if ($edit) {
-    // @todo Refactor the protection from editing unexisting forms.
-    if ($botcha_form instanceof BotchaFormNone) {
-      drupal_goto('admin/config/people/botcha/form/add', array('query' => array('botcha_form_id' => $botcha_form->id)));
-    }
+  if ($edit = !empty($botcha_form)) {
     $operation = 'edit';
     $recipebook = $botcha_form->getRecipebook();
     $botcha_form_id_default = check_plain($botcha_form->id);
+    // @todo Abstract it.
+    $validate = array();
     $button = t('Save');
   }
   else {
     $operation = 'add';
     // '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;
+    //$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.
@@ -306,16 +337,24 @@ function botcha_form_form($form, $form_state, $botcha_form = NULL) {
   );
   // Use passed as a parameter form id.
   $form['botcha_form_id'] = array(
-    '#type' => 'machine_name',
+    '#type' => 'textfield',
     '#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,
+    // @todo Abstract it.
+    //'#default_value' => $botcha_form_id_default,
+    //'#value' => $botcha_form_id_default,
+    '#required' => TRUE,
     '#disabled' => $edit,
     '#maxlength' => 128,
-    '#machine_name' => array(
-      'exists' => 'botcha_form_exists',
-    ),
+    '#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) {
@@ -328,17 +367,19 @@ function botcha_form_form($form, $form_state, $botcha_form = NULL) {
     '#default_value' => $recipebook->id,
     '#options' => $options,
   );
-  // @todo Why not just general submit field?
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array(
+  $form['submit'] = array(
     '#type' => 'submit',
     '#value' => $button,
   );
   return $form;
 }
 
-function botcha_form_exists($value) {
-  return !(Botcha::getForm($value, FALSE) instanceof BotchaFormNone);
+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)));
+  }
 }
 
 /**
@@ -361,32 +402,38 @@ function botcha_form_form_submit($form, &$form_state) {
       drupal_set_message(t('Added BOTCHA form %form_id.', array('%form_id' => $form_id)), 'status');
       break;
   }
-  $form_state['redirect'] = 'admin/config/people/botcha/form';
+  // @todo Abstract it.
+  //$form_state['redirect'] = 'admin/config/people/botcha/form';
+  $form_state['redirect'] = 'admin/user/botcha/form';
 }
 
 /**
  * Confirm dialog for deleting a BOTCHA form.
  */
-function botcha_form_delete_form($form, $form_state, $botcha_form = NULL) {
-//function botcha_point_disable_confirm($form, &$form_state, $botcha_point_form_id, $delete) {
-  $form = array();
-  $form['botcha_form_id'] = array(
-    '#type' => 'value',
-    '#value' => $botcha_form->id,
+function botcha_form_delete_form(&$form_state, $botcha_form) {
+  $form['#botcha_form'] = $botcha_form;
+  // @todo Abstract it.
+  //return confirm_form($form, $message, 'admin/config/people/botcha/form', NULL, t('Delete'));
+  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)),
+    'admin/user/botcha/form',
+    NULL,
+    t('Delete')
   );
-  $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, 'admin/config/people/botcha', 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'] = 'admin/config/people/botcha/form';
+  $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();
+  // @todo Abstract it.
+  //$form_state['redirect'] = 'admin/config/people/botcha/recipebook';
+  $form_state['redirect'] = 'admin/user/botcha/form';
 }
 
 /**
@@ -403,7 +450,9 @@ function botcha_forms_form() {
       '#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'),
+          // @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')
           ),
@@ -415,18 +464,23 @@ function botcha_forms_form() {
     '#theme' => 'botcha_forms_form_botcha_forms',
     '#tree' => TRUE,
   );
+  $form['botcha_form_protection']['botcha_form_id_overview']['#header'] = array('form_id', t('Operations'));
   $form['botcha_form_protection']['botcha_form_id_overview']['botcha_forms'] = array();
   $forms = Botcha::getForms(TRUE);
   foreach ($forms as $botcha_form) {
     $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,
+      // @todo Abstract it.
+      //'#markup' => $botcha_form->id,
+      '#value' => $botcha_form->id,
     );
     // Additional operations.
     $form['botcha_form_protection']['botcha_form_id_overview']['botcha_forms'][$botcha_form->id]['operations'] = array(
-      '#markup' => implode(" | ", array(
-        l(t('Edit'), "admin/config/people/botcha/form/{$botcha_form->id}"),
-        l(t('Delete'), "admin/config/people/botcha/form/{$botcha_form->id}/delete"),
+      // @todo Abstract it.
+      //'#markup' => implode(" | ", array(
+      '#value' => implode(" | ", array(
+        l(t('Edit'), "admin/user/botcha/form/{$botcha_form->id}"),
+        l(t('Delete'), "admin/user/botcha/form/{$botcha_form->id}/delete"),
       )),
     );
   }
@@ -477,10 +531,16 @@ function botcha_recipebooks_form() {
   $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'), 'admin/config/people/botcha/recipebook/' . $recipebook->id)
-      . (($recipebook->id == 'default') ? '' : ' | ' . l(t('Delete'), 'admin/config/people/botcha/recipebook/' . $recipebook->id . '/delete'));
+    $form['recipebooks'][$recipebook->id]['operations']['#markup'] = l(t('Edit'), 'admin/user/botcha/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'] = l(t('Edit'), 'admin/user/botcha/recipebook/' . $recipebook->id)
+      . (($recipebook->id == 'default') ? '' : ' | ' . l(t('Delete'), 'admin/user/botcha/recipebook/' . $recipebook->id . '/delete'));
   }
   return $form;
 }
@@ -489,45 +549,21 @@ function botcha_recipebooks_form() {
  * Theme botcha_recipebooks_form().
  */
 function theme_botcha_recipebooks_form($form) {
+  // @todo Abstract it.
+  // "Add recipebook" link.
+  $output = l(t('Add recipebook'), 'admin/user/botcha/recipebook/add');
+  // Prepare header before pass to theme.
+  $header = $form['#header'];
   // 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) {
-          /* @todo Remove it.
-          $row[$col] = array(
-            'data' => drupal_render($form['recipebooks'][$id][$col]),
-          );
-           *
-           */
-          //$row[] = drupal_render($form['recipebooks'][$id][$col]);
-        //}
-        $row[] = drupal_render($form['recipebooks'][$id]['title']);
-        $row[] = drupal_render($form['recipebooks'][$id]['description']);
-        $row[] = drupal_render($form['recipebooks'][$id]['operations']);
-        /* @todo Remove it.
-        $rows[] = array(
-          'data' => $row,
-        );
-         *
-         */
-        $rows[] = $row;
-      }
-  //  }
-  //}
-  $output = theme('table', array(
-    'header' => $form['#header'],
-    'rows' => $rows,
-    'empty' => t('No recipebooks available.'),
-  ));
-  /* @todo Remove it.
-  if (!empty($rows)) {
-    $output .= drupal_render($form);
+  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;
 }
 
diff --git a/botcha.inc b/botcha.inc
index 67fb805..46f5436 100644
--- a/botcha.inc
+++ b/botcha.inc
@@ -42,11 +42,15 @@ function _botcha_url($url, $options = array()) {
   $fragment = !empty($fragment) ? '#' . $fragment : '';
 
   if (is_array($query)) {
-    $query = drupal_http_build_query($query);
+    // @todo Abstract it.
+    // $query = drupal_http_build_query($query);
+    $query = drupal_query_string_encode($query);
   }
   $prefix = !empty($prefix) ? $prefix : '';
   $prefix = empty($path) ? rtrim($prefix, '/') : $prefix;
-  $path = drupal_encode_path($prefix . $path);
+  // @todo Abstract it.
+  //$path = drupal_encode_path($prefix . $path);
+  $path = drupal_urlencode($prefix . $path);
   if (variable_get('clean_url', '0')) {
     // With Clean URLs.
     $result = !empty($query) ? ($base . $path . '?' . $query . $fragment) : $base . $path . $fragment;
@@ -295,3 +299,25 @@ function _botcha_insert_botcha_element(&$form, $placement, $botcha_element) {
     }
   }
 }
+
+/**
+ * Process form callback for BOTCHA form.
+ * Using hooking mechanism that is a hack - we want to come in before #process,
+ * but our _form_alter() is way too early (we want to let it use cache for all other
+ * modules & data). Ideal would be to have #process callback
+ * work on the form, but FAPI does not call it on forms, despite the
+ * documentation stating that it does. In fact, D6 code needs '#input'=TRUE on
+ * the element to get into calling #process callbacks. So we set '#input'=>TRUE
+ * on the form when inserting our callback into form['#process'], and it forces
+ * the FAPI to call that code path and we get our intercept. Works like a charm!
+ */
+function botcha_fprocess($element, $edit, &$form_state, $complete_form) {
+  // Prevent caching of the page with BOTCHA elements.
+  // This needs to be done even if the BOTCHA will be ommitted later:
+  // other untrusted users should not get a cached page.
+  global $conf;
+  $conf['cache'] = CACHE_DISABLED;
+  // This temporarily disables cache (for this page request)
+  unset($element['#cache']);
+  return $element;
+}
diff --git a/botcha.install b/botcha.install
index 1f6d69a..22adf78 100644
--- a/botcha.install
+++ b/botcha.install
@@ -211,14 +211,6 @@ function _botcha_default_form_ids() {
 function botcha_install() {
   $t = get_t();
   drupal_install_schema('botcha');
-  /* @todo Remove it.
-  // Insert some default BOTCHA points.
-  $form_ids = _botcha_default_form_ids();
-  foreach ($form_ids as $form_id) {
-    db_query("INSERT INTO {botcha_points} (form_id, botcha_type) VALUES ('%s', 'default')", $form_id);
-  }
-   *
-   */
   $i18n_variables = variable_get('i18n_variables', '');
   if (!is_array($i18n_variables)) {
     $i18n_variables = array();
@@ -340,7 +332,11 @@ function botcha_update_6100() {
   $forms = _botcha_default_form_ids();
   foreach ($forms as $form_id) {
     db_query("INSERT INTO {botcha_form} (id) VALUES ('%s')", array($form_id));
-    db_query("INSERT INTO {botcha_recipebook_form} (rbid, form_id) VALUES ('default', '%s')", array($form_id));
+    // Leave login forms unprotected. It is very important, because if one of the
+    // recipes s broken (ie always blocks), admin must have opportunity to login.
+    if (!in_array($form_id, array('user_login', 'user_login_block'))) {
+      db_query("INSERT INTO {botcha_recipebook_form} (rbid, form_id) VALUES ('default', '%s')", array($form_id));
+    }
   }
   // Fill in botcha_recipe and botcha_recipebook_recipe.
   $recipes = array(
diff --git a/botcha.module b/botcha.module
index d98e512..42015e0 100644
--- a/botcha.module
+++ b/botcha.module
@@ -39,7 +39,9 @@ function botcha_help($path, $arg) {
 
 //      $output .= '<p>'. t('BOTCHA is a trademark of IVA2K.') .'</p>';
       return $output;
-    case 'admin/config/people/botcha':
+    // @todo Abstract it.
+    //case 'admin/config/people/botcha':
+    case 'admin/user/botcha':
       $output = '<p>' . t('A BOTCHA protection can be added to virtually each Drupal form. Some default forms are already provided in the form list and more can be added using form internal name.') . '</p>';
       $output .= '<p>' . t('All existing forms can be easily added and managed when the option "%adminlinks" is enabled.',
         array('%adminlinks' => t('Add BOTCHA administration links to forms'))) . '</p>';
@@ -51,8 +53,12 @@ function botcha_help($path, $arg) {
         array('%skipbotcha' => t('skip BOTCHA'), '@perm' => url('admin/people/permissions/list', array('fragment' => 'module-' . 'botcha')))) . '</p>';
       return $output;
     // !!+{
-    case 'admin/config/people/botcha/form':
-      $output = t('Select which forms to protect with BOTCHA. You can modify recipe books on <a href="@recipebook_page">Recipe books</a> page.', array('@recipebook_page' => url('admin/config/people/botcha/recipebook')));
+    // @todo Abstract it.
+    //case 'admin/config/people/botcha/form':
+    case 'admin/user/botcha/form':
+      // @todo Abstract it.
+      //$output = t('Select which forms to protect with BOTCHA. You can modify recipe books on <a href="@recipebook_page">Recipe books</a> page.', array('@recipebook_page' => url('admin/config/people/botcha/recipebook')));
+      $output = t('Select which forms to protect with BOTCHA. You can modify recipe books on <a href="@recipebook_page">Recipe books</a> page.', array('@recipebook_page' => url('admin/user/botcha/recipebook')));
       return $output;
     // !!+}
   }
@@ -93,13 +99,15 @@ function botcha_menu() {
     'page arguments' => array('botcha_form_form'),
     'access arguments' => array('administer BOTCHA settings'),
     'file' => 'botcha.admin.inc',
-    'type' => MENU_LOCAL_ACTION,
+    // @todo Abstract it.
+    //'type' => MENU_LOCAL_ACTION,
+    'type' => MENU_CALLBACK,
   );
   $items['admin/user/botcha/form/%botcha_form'] = array(
     'title' => 'BOTCHA form administration',
     'file' => 'botcha.admin.inc',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('botcha_form_form', 5),
+    'page arguments' => array('botcha_form_form', 4),
     'access arguments' => array('administer BOTCHA settings'),
     'type' => MENU_CALLBACK,
   );
@@ -115,7 +123,7 @@ function botcha_menu() {
   $items['admin/user/botcha/form/%botcha_form/delete'] = array(
     'title' => 'Delete',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('botcha_form_delete_form', 5),
+    'page arguments' => array('botcha_form_delete_form', 4),
     'access arguments' => array('administer BOTCHA settings'),
     'file' => 'botcha.admin.inc',
     'type' => MENU_LOCAL_TASK,
@@ -137,13 +145,15 @@ function botcha_menu() {
     'page arguments' => array('botcha_recipebook_form'),
     'access arguments' => array('administer BOTCHA settings'),
     'file' => 'botcha.admin.inc',
-    'type' => MENU_LOCAL_ACTION,
+    // @todo Abstract it.
+    //'type' => MENU_LOCAL_ACTION,
+    'type' => MENU_CALLBACK,
   );
   $items['admin/user/botcha/recipebook/%botcha_recipebook'] = array(
     'title callback' => 'botcha_recipebook_title',
-    'title arguments' => array(5),
+    'title arguments' => array(4),
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('botcha_recipebook_form', 5),
+    'page arguments' => array('botcha_recipebook_form', 4),
     'access arguments' => array('administer BOTCHA settings'),
     'file' => 'botcha.admin.inc',
     'type' => MENU_NORMAL_ITEM,
@@ -160,7 +170,7 @@ function botcha_menu() {
   $items['admin/user/botcha/recipebook/%botcha_recipebook/delete'] = array(
     'title' => 'Delete',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('botcha_recipebook_delete_form', 5),
+    'page arguments' => array('botcha_recipebook_delete_form', 4),
     'access arguments' => array('administer BOTCHA settings'),
     'file' => 'botcha.admin.inc',
     'type' => MENU_LOCAL_TASK,
@@ -181,7 +191,7 @@ function botcha_form_load($form_id) {
  * Load an object of recipe book.
  */
 function botcha_recipebook_load($rbid) {
-  return Botcha::getRecipebook($rbid);
+  return Botcha::getRecipebook($rbid, FALSE);
 }
 
 /**
@@ -194,17 +204,12 @@ function botcha_recipebook_title($recipebook) {
 }
 
 /**
- * Implements hook_permission().
+ * Implements hook_perm().
  */
-function botcha_permission() {
+function botcha_perm() {
   return array(
-    'administer BOTCHA settings' => array(
-      'title' => t('Administer BOTCHA settings'),
-    ),
-    'skip BOTCHA' => array(
-      'title' => t('Skip BOTCHA'),
-      'description' => t('Users with this permission will not be subjected to BOTCHA.'),
-    ),
+    'administer BOTCHA settings',
+    'skip BOTCHA',
   );
 }
 
@@ -214,11 +219,11 @@ function botcha_permission() {
 function botcha_theme() {
   return array(
     'botcha_forms_form_botcha_forms' => array(
-      'render element' => 'form',
+      'arguments' => array('form' => NULL),
       'file' => 'botcha.admin.inc',
     ),
     'botcha_recipebooks_form' => array(
-      'render element' => 'form',
+      'arguments' => array('form' => NULL),
       'file' => 'botcha.admin.inc',
     ),
   );
diff --git a/botcha.test b/botcha.test
index 34f0d7b..a4ee1cb 100644
--- a/botcha.test
+++ b/botcha.test
@@ -59,22 +59,28 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
   /**
    * Drupal path of the (general) BOTCHA admin page
    */
-  const BOTCHA_ADMIN_PATH = 'admin/config/people/botcha';
+  const BOTCHA_ADMIN_PATH = 'admin/user/botcha';
 
   function setUp() {
     // Load two modules: the botcha module itself and the comment module
     // for testing anonymous comments.
     parent::setUp(array('comment', 'botcha'));
-    // Disable all recipes for default recipe book not to have any problems with
-    // submitting forms.
+    // Disable all recipes and forms for default recipe book not to have any
+    // problems with submitting forms.
     $recipebook = Botcha::getRecipebook();
     foreach ($recipebook->getRecipes() as $recipe) {
-      $recipebook = $recipebook->unsetRecipe($recipe->id);
+      $recipebook->unsetRecipe($recipe->id);
+    }
+    foreach ($recipebook->getForms() as $form) {
+      $recipebook->unsetForm($form->id);
     }
     $recipebook->save();
     // Create a normal user.
     $permissions = array(
-      'access comments', 'post comments', 'skip comment approval',
+      'access comments', 'post comments',
+      // @todo Abstract it.
+      //'skip comment approval',
+      'post comments without approval',
       'access content', 'create page content', 'edit own page content',
     );
     $this->normal_user = $this->drupalCreateUser($permissions);
@@ -134,7 +140,9 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
   protected function createNodeWithCommentsEnabled($type='page') {
     $node_settings = array(
       'type' => $type,
-      'comment' => COMMENT_NODE_OPEN,
+      // @todo Abstract it.
+      //'comment' => COMMENT_NODE_OPEN,
+      'comment' => COMMENT_NODE_READ_WRITE,
     );
     $node = $this->drupalCreateNode($node_settings);
     return $node;
@@ -145,11 +153,14 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
    */
   protected function getCommentFormValuesFromForm() {
     // Submit the form using the displayed values.
-    $langcode = LANGUAGE_NONE;
+    // @todo Abstract it.
+    //$langcode = LANGUAGE_NONE;
     $displayed = array();
     foreach (array(
       'subject' => "//input[@id='edit-subject']/@value",
-      "comment_body[$langcode][0][value]" => "//textarea[@id='edit-comment-body-$langcode-0-value']",
+      // @todo Abstract it.
+      //"comment_body[$langcode][0][value]" => "//textarea[@id='edit-comment-body-$langcode-0-value']",
+      'comment' => "//textarea[@id='edit-comment-body-$langcode-0-value']",
       'botcha_response' => "//input[@id='edit-botcha-response']/@value",
     ) as $field => $path) {
       $value = current($this->xpath($path));
@@ -165,10 +176,13 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
    */
   // "= NULL" is for backward compatibility.
   protected function setCommentFormValues($should_pass = NULL) {
-    $langcode = LANGUAGE_NONE;
+    // @todo Abstract it.
+    //$langcode = LANGUAGE_NONE;
     $edit = array(
       'subject' => 'comment_subject ' . $this->randomName(32),
-      "comment_body[$langcode][0][value]" => 'comment_body ' . $this->randomName(256),
+      // @todo Abstract it.
+      //"comment_body[$langcode][0][value]" => 'comment_body ' . $this->randomName(256),
+      'comment' => 'comment_body ' . $this->randomName(256),
     );
     return $edit;
   }
@@ -178,7 +192,9 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
    */
   // "= NULL" is for backward compatibility.
   protected function setNodeFormValues($should_pass = NULL) {
-    $langcode = LANGUAGE_NONE;
+    // @todo Abstract it.
+    //$langcode = LANGUAGE_NONE;
+    $langcode = 'und';
     $edit = array(
       'title' => 'node_title ' . $this->randomName(32),
       "body[$langcode][0][value]" => 'node_body ' . $this->randomName(256),
@@ -217,9 +233,13 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
     $edit = array(
       '1[access comments]' => TRUE,
       '1[post comments]' => TRUE,
-      '1[skip comment approval]' => TRUE,
+      // @todo Abstract it.
+      //'1[skip comment approval]' => TRUE,
+      '1[post comments without approval]' => TRUE,
     );
-    $this->drupalPost('admin/people/permissions', $edit, 'Save permissions');
+    // @todo Abstract it.
+    //$this->drupalPost('admin/config/people/permissions', $edit, 'Save permissions');
+    $this->drupalPost('admin/user/permissions', $edit, 'Save permissions');
     $this->assertText('The changes have been saved.');
     // Log admin out
     $this->drupalLogout();
@@ -262,40 +282,51 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
     switch ($form) {
       // These ones for testing FormUI.
       case 'addForm':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $edit['botcha_form_id'] = drupal_strtolower($this->randomName(32));
         $parameters['botcha_form_id'] = $edit['botcha_form_id'];
         $edit["botcha_form_recipebook"] = $parameters['rbid'];
         break;
       case 'editForm':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
+        //$edit['botcha_form_id'] = $parameters['botcha_form_id'];
         $edit["botcha_form_recipebook"] = $parameters['rbid'];
         break;
       case 'deleteForm':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         // Nothing to do.
         break;
       // These ones for testing RecipebookUI.
       case 'addRecipebook':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $edit['id'] = drupal_strtolower($this->randomName(32));
         $edit['title'] = $this->randomName(32);
         $edit['description'] = $this->randomName(255);
         $edit['recipes[timegate]'] = 'timegate';
         break;
       case 'editRecipebook':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
+        //$edit['id'] = $parameters['id'];
         $edit['title'] = $this->randomName(32);
         $edit['description'] = $this->randomName(255);
         $edit['recipes[timegate]'] = 'timegate';
         break;
       case 'deleteRecipebook':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         // @todo BotchaBaseWebTestCase setFormValues Case deleteRecipebook real logic.
         break;
       // And these ones for testing form submission.
       case 'node':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $edit = $this->setNodeFormValues($should_pass);
         break;
       case 'user_login':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $edit = $this->setUserLoginFormValues($should_pass);
         break;
       case 'comment':
       default:
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $edit = $this->setCommentFormValues($should_pass);
         break;
     }
@@ -321,12 +352,16 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
     switch ($form) {
       // These ones for testing FormUI.
       case 'addForm':
-        $this->drupalGet('admin/config/people/botcha/form/add');
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
+        // @todo Abstract it.
+        //$this->drupalGet('admin/config/people/botcha/form/add');
+        $this->drupalGet('admin/user/botcha/form/add');
         foreach (array('botcha_form_id', 'botcha_form_recipebook') as $field) {
           $this->assertField($field, "There should be a $field field on the form", 'BOTCHA');
         }
         break;
       case 'editForm':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $form_id = drupal_strtolower($this->randomName(12));
         // Pass this newly created form to other methods.
         $parameters['botcha_form_id'] = $form_id;
@@ -337,30 +372,41 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
         // Assert recipe book of the form.
         $recipebook = Botcha::getForm($parameters['botcha_form_id'], FALSE)->getRecipebook();
         $this->assertEqual('default', $recipebook->id, "BOTCHA form has recipe book {$recipebook->id} (should have default)", 'BOTCHA');
-        $this->drupalGet("admin/config/people/botcha/form/$form_id");
+        // @todo Abstract it.
+        //$this->drupalGet("admin/config/people/botcha/form/$form_id");
+        $this->drupalGet("admin/user/botcha/form/$form_id");
         foreach (array('botcha_form_id', 'botcha_form_recipebook') as $field) {
           $this->assertField($field, "There should be a $field field on the form", 'BOTCHA');
         }
         // @todo getForm Check that id field is disabled.
         break;
       case 'deleteForm':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $form_id = drupal_strtolower($this->randomName(12));
         // Pass this newly created form to other methods.
         $parameters['botcha_form_id'] = $form_id;
         // The form should be already binded to some (let's say default) recipe book.
-        Botcha::getForm($form_id)->setRecipebook('default')->save();
-        $this->drupalGet("admin/config/people/botcha/form/$form_id/delete");
+        Botcha::getForm($form_id, TRUE)->setRecipebook('default')->save();
+        // @todo Abstract it.
+        //$this->drupalGet("admin/config/people/botcha/form/$form_id/delete");
+        $this->drupalGet("admin/user/botcha/form/$form_id/delete");
         break;
       // These ones for testing RecipebookUI.
       case 'addRecipebook':
-        $this->drupalGet('admin/config/people/botcha/recipebook/add');
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
+        // @todo Abstract it.
+        //$this->drupalGet('admin/config/people/botcha/recipebook/add');
+        $this->drupalGet('admin/user/botcha/recipebook/add');
         // @todo BotchaBaseWebTestCase getForm Implement recipes checking.
         foreach (array('id', 'title', 'description') as $field) {
           $this->assertField($field, "There should be a $field field on the form", 'BOTCHA');
         }
         break;
       case 'editRecipebook':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $id = drupal_strtolower($this->randomName(12));
+        // Save this id to the parameters.
+        $parameters['id'] = $id;
         $title = $this->randomName(12);
         $description = $this->randomString(255);
         // We need some recipes already set to test unsetting.
@@ -370,33 +416,40 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
           ->setDescription($description)
           ->setRecipe($recipe_id)
           ->save();
-        //$this->createRecipebook($id, $title, $description, $recipes);
-        $this->drupalGet("admin/config/people/botcha/recipebook/$id");
+        // @todo Abstract it.
+        //$this->drupalGet("admin/config/people/botcha/recipebook/$id");
+        $this->drupalGet("admin/user/botcha/recipebook/$id");
         // @todo BotchaBaseWebTestCase getForm Implement recipes appearance checking.
         foreach (array('id', 'title', 'description') as $field) {
           $this->assertField($field, "There should be a $field field on the form", 'BOTCHA');
         }
         break;
       case 'deleteRecipebook':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         // @todo BotchaBaseWebTestCase getForm Case deleteRecipebook real logic.
         break;
       // And these ones are for testing form submissions.
       case 'node':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $this->drupalGet('node/add/page');
         $this->assertBotchaPresence(TRUE);
         break;
       case 'user_login':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $this->drupalGet('user');
         $this->assertBotchaPresence(TRUE);
         break;
       case 'comment':
       default:
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         // Create node to post comment to.
         $node = $this->createNodeWithCommentsEnabled();
-        $this->drupalGet("comment/reply/$node->nid");
+        $this->drupalGet("comment/reply/{$node->nid}");
         $this->assertBotchaPresence(TRUE);
-        // Make sure comments on pages can be saved directely without preview.
-        variable_set('comment_preview_page', DRUPAL_OPTIONAL);
+        // Make sure comments on pages can be saved directly without preview.
+        // @todo Abstract it.
+        //variable_set('comment_preview_page', DRUPAL_OPTIONAL);
+        variable_set('comment_preview_page', 0);
         break;
     }
   }
@@ -409,42 +462,53 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
     switch ($form) {
       // These ones for testing Form UI.
       case 'addForm':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $button = ($button) ? $button : t('Add');
         $this->drupalPost(NULL, $edit, $button);
         break;
       case 'editForm':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $button = ($button) ? $button : t('Save');
         $this->drupalPost(NULL, $edit, $button);
         break;
       case 'deleteForm':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $button = ($button) ? $button : t('Delete');
         $this->drupalPost(NULL, $edit, $button);
         break;
       // These ones for testing Recipebook UI.
       case 'addRecipebook':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $button = ($button) ? $button : t('Add');
         $this->drupalPost(NULL, $edit, $button);
         break;
       case 'editRecipebook':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $button = ($button) ? $button : t('Save');
         $this->drupalPost(NULL, $edit, $button);
         break;
       case 'deleteRecipebook':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         // @todo BotchaBaseWebTestCase postForm Case deleteRecipebook.
         break;
       // And these ones are for testing form submissions.
       case 'node':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $button = ($button) ? $button : t('Save');
         $this->drupalPost(NULL, $edit, $button);
         break;
       case 'user_login':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $button = ($button) ? $button : t('Log in');
         $this->drupalPost(NULL, $edit, $button);
         break;
       case 'comment':
       default:
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         // Make sure comments on pages can be saved directly without preview.
-        variable_set('comment_preview_page', DRUPAL_OPTIONAL);
+        // @todo Abstract it.
+        //variable_set('comment_preview_page', DRUPAL_OPTIONAL);
+        variable_set('comment_preview_page', 0);
         $button = ($button) ? $button : t('Save');
         $this->drupalPost(NULL, $edit, $button);
         break;
@@ -458,6 +522,7 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
     switch ($form) {
       // These ones for testing Form UI.
       case 'addForm':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         // Make sure that the message appeared.
         //$this->assertText(t('Added BOTCHA form %form_id.', array('%form_id' => $parameters['botcha_form_id'])), 'BOTCHA form successfully added : Message displayed', 'BOTCHA');
         $this->assertText("Added BOTCHA form {$parameters['botcha_form_id']}.", 'BOTCHA form successfully added : Message displayed', 'BOTCHA');
@@ -468,6 +533,7 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
         $this->assertEqual($parameters['rbid'], $recipebook->id, 'BOTCHA form successfully added : Recipe book saved correctly', 'BOTCHA');
         break;
       case 'editForm':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         // Make sure that the message appeared.
         $this->assertText("Saved BOTCHA form settings for {$parameters['botcha_form_id']}.", 'BOTCHA form successfully saved : Message displayed', 'BOTCHA');
         // Assert recipe book of the form.
@@ -475,6 +541,7 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
         $this->assertEqual($parameters['rbid'], $recipebook->id, 'BOTCHA form successfully saved : Recipe book saved correctly', 'BOTCHA');
         break;
       case 'deleteForm':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         // Make sure that the message appeared.
         $this->assertText("Deleted BOTCHA protection for form {$parameters['botcha_form_id']}.", 'BOTCHA form successfully deleted : Message displayed', 'BOTCHA');
         // Ensure that the form was deleted.
@@ -482,58 +549,51 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
         break;
       // These ones for testing Recipebook UI.
       case 'addRecipebook':
-        $select = db_select('botcha_recipebook', 'brb')
-          ->fields('brb')
-          ->condition('id', $edit['id'])
-          ->execute()
-          ->fetchAllAssoc('id');
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
+        // @todo Port this to D7.
         $this->assertTrue(
-          count($select),
+          $recipebook = Botcha::getRecipebook($edit['id'], FALSE),
           "Recipe book {$edit['id']} should exist",
           'BOTCHA'
         );
-        $existent = current($select);
         foreach (array('id', 'title', 'description') as $field) {
           $this->assertEqual(
-            $existent->$field,
+            $recipebook->$field,
             $edit[$field],
-            "Recipe book {$edit['id']} should have $field equal $edit[$field] (in fact it has {$existent->$field})",
+            "Recipe book {$edit['id']} should have $field equal $edit[$field] (in fact it has {$recipebook->$field})",
             'BOTCHA'
           );
         }
         break;
       case 'editRecipebook':
-        // @todo BotchaBaseWebTestCase assertFormSubmission Reduce code duplication.
-        // @todo BotchaBaseWebTestCase assertFormSubmission Find a better way to pass recipe book id from getForm to assertFormSubmission.
-        $edit['id'] = current($this->xpath('//*[@id="messages"]/div/div/em/text()'));
-        $select = db_select('botcha_recipebook', 'brb')
-          ->fields('brb')
-          ->condition('id', $edit['id'])
-          ->execute()
-          ->fetchAllAssoc('id');
-        $existent = current($select);
-        foreach (array('id', 'title', 'description') as $field) {
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
+        $recipebook = Botcha::getRecipebook($parameters['id'], FALSE);
+        foreach (array('title', 'description') as $field) {
           $this->assertEqual(
-            $existent->$field,
+            $recipebook->$field,
             $edit[$field],
-            "Recipe book {$edit['id']} should have $field equal $edit[$field] (in fact it has {$existent->$field})",
+            "Recipe book {$parameters['id']} should have $field equal $edit[$field] (in fact it has {$recipebook->$field})",
             'BOTCHA'
           );
         }
         // @todo Add recipes assertion.
         break;
       case 'deleteRecipebook':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         // @todo BotchaBaseWebTestCase assertFormSubmission Case deleteRecipebook.
         break;
       // And these ones are for testing form submissions.
       case 'node':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $this->assertNodeFormSubmission($edit, $should_pass, $button);
         break;
       case 'user_login':
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $this->assertUserLoginFormSubmission($edit, $should_pass, $button);
         break;
       case 'comment':
       default:
+        $this->debug("Entered %method %case", array('%method' => __METHOD__, '%case' => $form));
         $this->assertCommentFormSubmission($edit, $should_pass, $button);
         break;
     }
@@ -560,7 +620,7 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
           'Comment submission should be blocked.', 'BOTCHA');
         // Check that there is still BOTCHA after failed submit.
         $this->assertBotchaPresence(TRUE);
-        $this->assertNoText($edit['subject'], 'Comment should not show up on node page.', 'BOTCHA');
+        //$this->assertNoText($edit['subject'], 'Comment should not show up on node page.', 'BOTCHA');
         // !!? Do we need to check message body?
         //$this->assertNoText($comment_body, $message . ' Comment should not show up on node page.', 'BOTCHA');
         break;
@@ -619,6 +679,15 @@ abstract class BotchaBaseWebTestCase extends DrupalWebTestCase {
         break;
     }
   }
+
+  /**
+   * Used to print debug message on the test result screen.
+   * @param string $message_template
+   * @param array $substitutions
+   */
+  protected function debug($message_template, $substitutions = array()) {
+    return $this->assertTrue(TRUE, t($message_template, $substitutions), 'BOTCHA');
+  }
 }
 
 
@@ -645,7 +714,9 @@ class BotchaAdminTestCase extends BotchaBaseWebTestCase {
     $this->drupalPost(self::BOTCHA_ADMIN_PATH . '/form', $edit, 'Save configuration');
     // Create a node with comments enabled.
     $node = $this->createNodeWithCommentsEnabled();
-    $form_id = 'comment_node_page_form';
+    // @todo Abstract it.
+    //$form_id = 'comment_node_page_form';
+    $form_id = 'comment_form';
     // Go to node page
     $this->drupalGet('node/' . $node->nid);
     // Click the add new comment link
@@ -745,11 +816,14 @@ class BotchaAdminTestCase extends BotchaBaseWebTestCase {
       'editRecipebook',
       'deleteRecipebook',
     );
+    // @todo Port to D7.
+    // Parameters that need to be passed through methods.
+    $parameters = array();
     foreach ($forms as $form) {
-      $this->getForm($form);
-      $edit = $this->setFormValues($form);
-      $this->postForm($form, $edit);
-      $this->assertFormSubmission($form, $edit);
+      $this->getForm($form, $parameters);
+      $edit = $this->setFormValues($form, NULL, $parameters);
+      $this->postForm($form, $edit, NULL, $parameters);
+      $this->assertFormSubmission($form, $edit, NULL, NULL, $parameters);
     }
     $this->drupalLogout();
   }
@@ -762,23 +836,18 @@ class BotchaAdminTestCase extends BotchaBaseWebTestCase {
 class BotchaTestCase extends BotchaBaseWebTestCase {
   function setUp() {
     parent::setUp();
-    // Create recipebook "test" + bind all forms to it.
-    $forms = db_select('botcha_form', 'bf')
-      ->fields('bf', array('id'))
-      ->execute()
-      ->fetchCol();
+    // @todo Port to D7.
     // For some reason we don't find this form after installation - but we should.
     // So fix it manually.
-    $forms[] = 'comment_node_page_form';
     Botcha::getForm('comment_node_page_form', TRUE)->save();
+    // Create recipebook "test" + bind all forms to it.
     $recipebook = Botcha::getRecipebook('test', TRUE)
       ->setTitle('Test recipebook')
       ->setDescription("Created for {$this->testId}");
-    foreach ($forms as $form_id) {
-      $recipebook->setForm($form_id);
+    foreach (Botcha::getForms() as $form) {
+      $recipebook->setForm($form->id);
     }
     $recipebook->save();
-    //$this->createRecipebook('test', 'Test recipebook', "Created for {$this->testId}", array(), $forms);
   }
 
   function testFormSubmission() {
@@ -888,11 +957,10 @@ class BotchaHoneypotTestCase extends BotchaUsingJsTestCase {
   function setUp() {
     parent::setUp();
     // Bind only one recipe to test recipe book.
-    db_insert('botcha_recipebook_recipe')
-      ->fields(array(
-        'rbid' => 'test',
-        'recipe_id' => 'honeypot',
-      ))->execute();
+    // @todo Port to D7 also.
+    Botcha::getRecipebook('test')
+      ->setRecipe('honeypot')
+      ->save();
   }
 
   function getExpectations() {
@@ -922,11 +990,10 @@ class BotchaHoneypot2TestCase extends BotchaUsingJsTestCase {
   function setUp() {
     parent::setUp();
     // Bind only one recipe to test recipe book.
-    db_insert('botcha_recipebook_recipe')
-      ->fields(array(
-        'rbid' => 'test',
-        'recipe_id' => 'honeypot2',
-      ))->execute();
+    // @todo Port to D7 also.
+    Botcha::getRecipebook('test')
+      ->setRecipe('honeypot2')
+      ->save();
   }
 
   function getExpectations() {
@@ -956,11 +1023,10 @@ class BotchaObscureUrlTestCase extends BotchaUsingJsTestCase {
   function setUp() {
     parent::setUp();
     // Bind only one recipe to test recipe book.
-    db_insert('botcha_recipebook_recipe')
-      ->fields(array(
-        'rbid' => 'test',
-        'recipe_id' => 'obscure_url',
-      ))->execute();
+    // @todo Port to D7 also.
+    Botcha::getRecipebook('test')
+      ->setRecipe('obscure_url')
+      ->save();
   }
 
   function getExpectations() {
@@ -990,11 +1056,10 @@ class BotchaNoResubmitTestCase extends BotchaTestCase {
   function setUp() {
     parent::setUp();
     // Bind only one recipe to test recipe book.
-    db_insert('botcha_recipebook_recipe')
-      ->fields(array(
-        'rbid' => 'test',
-        'recipe_id' => 'no_resubmit',
-      ))->execute();
+    // @todo Port to D7 also.
+    Botcha::getRecipebook('test')
+      ->setRecipe('no_resubmit')
+      ->save();
   }
 
   function getExpectations() {
@@ -1083,11 +1148,10 @@ class BotchaTimegateTestCase extends BotchaTestCase {
   function setUp() {
     parent::setUp();
     // Bind only one recipe to test recipe book.
-    db_insert('botcha_recipebook_recipe')
-      ->fields(array(
-        'rbid' => 'test',
-        'recipe_id' => 'timegate',
-      ))->execute();
+    // @todo Port to D7 also.
+    Botcha::getRecipebook('test')
+      ->setRecipe('timegate')
+      ->save();
   }
 
   function getExpectations() {
diff --git a/controller/botcha.controller.inc b/controller/botcha.controller.inc
index 24c61e5..c406a93 100644
--- a/controller/botcha.controller.inc
+++ b/controller/botcha.controller.inc
@@ -32,15 +32,18 @@ class Botcha {
    */
   public static function getForms($reset = FALSE) {
     // Get the value from the cache.
-    if (empty(self::$forms) || $reset) {
+    //if (empty(self::$forms) || $reset) {
+      $forms = array();
       $fs = array_keys(BotchaFormModel::getForms());
       foreach ($fs as $form_id) {
         // Save form to our cache.
-        self::$forms[$form_id] = Botcha::getForm($form_id);
+        //self::$forms[$form_id] = Botcha::getForm($form_id);
+        $forms[$form_id] = Botcha::getForm($form_id);
       }
       //self::$recipebooks = &drupal_static('botcha_recipebooks', self::$recipebooks);
-    }
-    return self::$forms;
+    //}
+    //return self::$forms;
+      return $forms;
   }
 
   /**
@@ -52,9 +55,11 @@ class Botcha {
   public static function getForm($form_id, $create = TRUE) {
     // @todo This static cache is buggy. Remake it if we need it or delete else.
     //if (empty(self::$forms[$form_id])) {
-      if ($form = BotchaForm::getForm($form_id, $create)) {
+      $form = BotchaForm::getForm($form_id, $create);
+      if (!($form instanceof BotchaFormNone)) {
         // Set relationships for this concrete form.
-        foreach ((array) BotchaModel::getFormsRecipebooks(array(
+        foreach (BotchaModel::getFormsRecipebooks(array(
+            'mode' => 'recipebook',
             'forms' => $form_id,
           )) as $rbid) {
             $form = $form->setRecipebook($rbid);
@@ -87,15 +92,18 @@ class Botcha {
     // @todo Botcha getRecipebooks Find a better way to check whether we need to reset a list of all recipe books or not.
     //   The problem is in the case when we have one recipe book cached - and it
     //   makes unusable our cache since it does not have all recipe books.
-    if (empty(self::$recipebooks) || $reset) {
+    //if (empty(self::$recipebooks) || $reset) {
+      $recipebooks = array();
       $rbs = array_keys(BotchaRecipebookModel::getRecipebooks());
       foreach ($rbs as $rbid) {
         // Save recipe book to our cache.
-        self::$recipebooks[$rbid] = Botcha::getRecipebook($rbid);
+        //self::$recipebooks[$rbid] = Botcha::getRecipebook($rbid);
+        $recipebooks[$rbid] = Botcha::getRecipebook($rbid);
       }
       //self::$recipebooks = &drupal_static('botcha_recipebooks', self::$recipebooks);
-    }
-    return self::$recipebooks;
+    //}
+    //return self::$recipebooks;
+      return $recipebooks;
   }
 
   /**
@@ -116,7 +124,8 @@ class Botcha {
       if (!($recipebook instanceof BotchaRecipebookNone)) {
         // Get recipe book relationships.
         // Get recipe book - form relationships.
-        foreach ((array) BotchaModel::getRecipebooksForms(array(
+        // @todo Port to D7.
+        foreach (BotchaModel::getRecipebooksForms(array(
               'mode' => 'form',
               'recipebooks' => $id,
             )) as $form_id) {
@@ -154,16 +163,19 @@ class Botcha {
    */
   public static function getRecipes($reset = FALSE) {
     // Get the value from the cache.
-    self::$recipes = &drupal_static('botcha_recipes');
-    if (empty(self::$recipes) || $reset) {
-      $recipes = array_keys(BotchaRecipeModel::getRecipes());
-      foreach ($recipes as $recipe_id) {
+    //self::$recipes = &drupal_static('botcha_recipes');
+    //if (empty(self::$recipes) || $reset) {
+      $recipes = array();
+      $rs = array_keys(BotchaRecipeModel::getRecipes());
+      foreach ($rs as $recipe_id) {
         // Save recipe to our cache.
-        self::$recipes[$recipe_id] = Botcha::getRecipe($recipe_id);
+        //self::$recipes[$recipe_id] = Botcha::getRecipe($recipe_id);
+        $recipes[$recipe_id] = Botcha::getRecipe($recipe_id);
       }
       //self::$recipes = &drupal_static('botcha_recipes', self::$recipes);
-    }
-    return self::$recipes;
+    //}
+    //return self::$recipes;
+      return $recipes;
   }
 
   /**
diff --git a/controller/botcha_recipe.controller.inc b/controller/botcha_recipe.controller.inc
index e0273b3..8737616 100644
--- a/controller/botcha_recipe.controller.inc
+++ b/controller/botcha_recipe.controller.inc
@@ -260,7 +260,9 @@ abstract class BotchaRecipe {
       'getCss'
     );
     if (!empty($css)) {
-      drupal_add_css('' . $this->settings['css'] . '', array('type' => 'inline'));
+      // @todo Abstract it.
+      //drupal_add_css('' . $this->settings['css'] . '', array('type' => 'inline'));
+      drupal_set_html_head('<style type="text/css">' . $css . '</style>');
     }
     return array();
   }
@@ -326,10 +328,14 @@ class BotchaRecipeNoResubmit extends BotchaRecipe {
   }
 
   protected function getToken($value = '') {
+    /* @todo Port to D7.
     if (empty($_SESSION['botcha_session'])) {
       $_SESSION['botcha_session'] = session_id();
     }
     return drupal_hmac_base64($value, $_SESSION['botcha_session'] . drupal_get_private_key() . drupal_get_hash_salt());
+     *
+     */
+    return drupal_get_token($value);
   }
 
   function applyRecipe(&$form, &$form_state) {
@@ -351,7 +357,9 @@ class BotchaRecipeNoResubmit extends BotchaRecipe {
     $data['#cache_token'] = $this->getToken();
     // We use cache_form table.
     // Sneaky, but why build our own table since we are working side-by-side with form API?
-    cache_set('botcha_' . $build_id, $data, 'cache_form', REQUEST_TIME + $expire);
+    // @todo Abstract it.
+    //cache_set('botcha_' . $build_id, $data, 'cache_form', REQUEST_TIME + $expire);
+    cache_set('botcha_' . $build_id, $data, 'cache_form', $_SERVER['REQUEST_TIME'] + $expire);
   }
 
   function handle($mode, $form, $form_state) {
@@ -363,7 +371,9 @@ class BotchaRecipeNoResubmit extends BotchaRecipe {
     // Make it to expire immediately.
     $expire = 0;
     $data = array();
-    cache_set('botcha_' . $build_id, $data, 'cache_form', REQUEST_TIME + $expire);
+    // @todo Abstract it.
+    //cache_set('botcha_' . $build_id, $data, 'cache_form', REQUEST_TIME + $expire);
+    cache_set('botcha_' . $build_id, $data, 'cache_form', $_SERVER['REQUEST_TIME'] + $expire);
   }
 }
 
@@ -402,7 +412,9 @@ class BotchaRecipeUsingJsAbstract extends BotchaRecipe {
       ,
         // @todo Move it to constant since it is also used in error_text.
         '#suffix' => '</div>' . '<noscript>' . t('Please enable Javascript to use this form.') . '</noscript>',
-        '#attributes' => array('class' => array($fields[0]['class']), 'autocomplete' => 'off'),
+        // @todo Abstract it.
+        //'#attributes' => array('class' => array($fields[0]['class']), 'autocomplete' => 'off'),
+        '#attributes' => array('class' => $fields[0]['class'], 'autocomplete' => 'off'),
         '#weight' => -20,
         '!valid_token' => $js['secure_token'],
       ),
@@ -412,7 +424,9 @@ class BotchaRecipeUsingJsAbstract extends BotchaRecipe {
       'getJsValue'
     );
     if (!empty($js_value)) {
-      drupal_add_js($js_value, array('type' => 'inline', 'preprocess' => FALSE));
+      // @todo Abstract it.
+      //drupal_add_js($js_value, array('type' => 'inline', 'preprocess' => FALSE));
+      drupal_add_js($js_value, 'inline');
     }
     return array_merge(parent::generateFormElements(), $form_elements);
   }
@@ -783,6 +797,8 @@ END;
         '#type' => 'hidden',
         // Store part of secure_token.
         '#default_value' => $fields[1]['default_value'],
+        // @todo Abstract it.
+        //'#attributes' => array('class' => array($fields[1]['class'])),
         '#attributes' => array('class' => $fields[1]['class']),
         '#weight' => 20,
       ),
@@ -862,12 +878,23 @@ class BotchaRecipeTimegate extends BotchaRecipe {
   function isSpam($form, $form_state) {
     $isSpam = parent::isSpam($form, $form_state);
     // Timegate method validation.
+    /* @todo Port this to D7.
     $absence = empty($form['timegate'])
       || empty($form_state['values']['timegate'])
       || empty($form_state['post']['timegate']);
     $minimal_delay = variable_get('botcha_timegate', 8);
     $form_generated = $form_state['values']['timegate'];
     $form_submitted = $form_state['post']['timegate'];
+     *
+     */
+    $absence = empty($form['timegate']);
+    $minimal_delay = variable_get('botcha_timegate', 8);
+    // @todo Abstract it.
+    //$form_generated = (!empty($form_state['values']['timegate'])) ? $form_state['values']['timegate'] : NULL;
+    $form_generated = (!empty($form_state['botcha_submit_values']['timegate'])) ? $form_state['botcha_submit_values']['timegate'] : NULL;
+    // @todo Abstract it.
+    //$form_submitted = (!empty($form_state['post']['timegate'])) ? $form_state['post']['timegate'] : NULL;
+    $form_submitted = (!empty($form_state['values']['timegate'])) ? $form_state['values']['timegate'] : NULL;
     if ($absence || ((int)$form_submitted < ((int)$form_generated + (int)$minimal_delay))) {
       $isSpam = TRUE;
     }
diff --git a/controller/botcha_recipebook.controller.inc b/controller/botcha_recipebook.controller.inc
index cdd244e..46bff66 100644
--- a/controller/botcha_recipebook.controller.inc
+++ b/controller/botcha_recipebook.controller.inc
@@ -69,7 +69,9 @@ class BotchaRecipebook {
         '#title' => t('BOTCHA'),
         '#collapsible' => TRUE,
         '#collapsed' => TRUE,
-        '#attributes' => array('class' => array('botcha-admin-links')),
+        // @todo Abstract it.
+        //'#attributes' => array('class' => array('botcha-admin-links')),
+        '#attributes' => array('class' => 'botcha-admin-links'),
       );
       if (!($recipebook instanceof BotchaRecipebookNone)) {
       // !!- if ($botcha_point !== NULL && $botcha_point->botcha_type  && $botcha_point->botcha_type != 'none') {
@@ -77,32 +79,38 @@ class BotchaRecipebook {
         // !!- $botcha_element['#title'] = t('BOTCHA: protection enabled (@type cookbook)', array('@type' => $botcha_point->botcha_type));
         // !!- $botcha_element['#description'] = t('Untrusted users will have form %form_id protected by BOTCHA (!settings).',
         $botcha_element['#description'] = t('Untrusted users will have form %form_id protected by BOTCHA (!recipebook_settings, !general_settings).',
-          // !!- array('%form_id' => $form_id, '!settings' => l(t('general BOTCHA settings'), 'admin/config/people/botcha'))
           array(
             '%form_id' => $form_id,
-            '!recipebook_settings' => l(t('Recipe book settings'), "admin/config/people/botcha/recipebook/{$recipebook->id}"),
-            '!general_settings' => l(t('General BOTCHA settings'), 'admin/config/people/botcha'),
+            // @todo Abstract it.
+            //'!recipebook_settings' => l(t('Recipe book settings'), "admin/config/people/botcha/recipebook/{$recipebook->id}"),
+            '!recipebook_settings' => l(t('Recipe book settings'), "admin/user/botcha/recipebook/{$recipebook->id}"),
+            // @todo Abstract it.
+            //'!general_settings' => l(t('General BOTCHA settings'), 'admin/config/people/botcha'),
+            '!general_settings' => l(t('General BOTCHA settings'), 'admin/user/botcha'),
           )
         );
         $botcha_element['protection'] = array(
           '#type' => 'item',
           '#title' => t('Enabled protection'),
-          '#markup' => t('Form is protected by "@recipebook" recipe book (!edit, !delete)', array(
-          // !!- '#markup' => t('"@type" cookbook (!change, !disable)', array(
+          // @todo Abstract it.
+          //'#markup' => t('Form is protected by "@recipebook" recipe book (!edit, !delete)', array(
+          '#value' => t('Form is protected by "@recipebook" recipe book (!edit, !delete)', array(
             '@recipebook' => $recipebook->id,
-            // !!- '@type' => $botcha_point->botcha_type,
-            '!edit' => l(t('edit'), "admin/config/people/botcha/form/$form_id", array('query' => drupal_get_destination(), 'html' => TRUE)),
-            // !!- '!change' => l(t('change'), "admin/config/people/botcha/botcha_point/$form_id", array('query' => drupal_get_destination(), 'html' => TRUE)),
-            '!delete' => l(t('delete'), "admin/config/people/botcha/form/$form_id/delete", array('query' => drupal_get_destination(), 'html' => TRUE)),
-            // !!- '!disable' => l(t('disable'), "admin/config/people/botcha/botcha_point/$form_id/disable", array('query' => drupal_get_destination(), 'html' => TRUE)),
+            // @todo Abstract it.
+            //'!edit' => l(t('edit'), "admin/config/people/botcha/form/$form_id", array('query' => drupal_get_destination(), 'html' => TRUE)),
+            '!edit' => l(t('edit'), "admin/user/botcha/form/$form_id", array('query' => drupal_get_destination(), 'html' => TRUE)),
+            // @todo Abstract it.
+            //'!delete' => l(t('delete'), "admin/config/people/botcha/form/$form_id/delete", array('query' => drupal_get_destination(), 'html' => TRUE)),
+            '!delete' => l(t('delete'), "admin/user/botcha/form/$form_id/delete", array('query' => drupal_get_destination(), 'html' => TRUE)),
           )),
         );
       }
       else {
         $botcha_element['#title'] = t('BOTCHA: no protection enabled');
         $botcha_element['add_botcha'] = array(
-          '#markup' => l(t('Add BOTCHA protection on form'), "admin/config/people/botcha/form/add", array('query' => array_merge(drupal_get_destination(), array('botcha_form_id' => $form_id)), 'html' => TRUE)),
-          // !!- '#markup' => l(t('Add BOTCHA protection on form %form_id for untrusted users.', array('%form_id' => $form_id)), "admin/config/people/botcha/botcha_point/$form_id", array('query' => drupal_get_destination(), 'html' => TRUE)),
+          // @todo Abstract it.
+          //'#markup' => l(t('Add BOTCHA protection on form'), "admin/config/people/botcha/form/add", array('query' => array_merge(drupal_get_destination(), array('botcha_form_id' => $form_id)), 'html' => TRUE)),
+          '#value' => l(t('Add BOTCHA protection on form'), "admin/user/botcha/form/add", array('query' => drupal_get_destination() . "&botcha_form_id=$form_id", 'html' => TRUE)),
         );
       }
       // Get placement in form and insert in form.
@@ -117,7 +125,7 @@ class BotchaRecipebook {
     switch ($form_id) {
       case 'user_register':
         // Only change the registration form. There is also 'user_register' form
-        // at /admin/config/people/user/create path, but we leave it alone.
+        // at /admin/user/user/create path, but we leave it alone.
         if (FALSE === strpos($form['#action'], 'user/register')) {
           if (!variable_get('botcha_allow_on_admin_pages', FALSE)) {
             $isApplicable = FALSE;
@@ -181,13 +189,12 @@ class BotchaRecipebook {
    */
   function getRecipes() {
     $recipes = array();
-    // @todo BotchaRecipebook getRecipes Move it to model layer.
-    if (!isset($this->recipes)) {
-      $this->recipes = db_select('botcha_recipebook_recipe', 'brr')
-        ->fields('brr', array('recipe_id'))
-        ->condition('rbid', $this->id)
-        ->execute()
-        ->fetchCol();
+    if (empty($this->recipes)) {
+      // @todo Port this to D7.
+      $this->recipes = BotchaModel::getRecipebooksRecipes(array(
+        'mode' => 'recipe',
+        'recipebooks' => $this->id,
+      ));
     }
     foreach ($this->recipes as $recipe_id) {
       $recipes[$recipe_id] = Botcha::getRecipe($recipe_id);
@@ -200,19 +207,25 @@ class BotchaRecipebook {
     return $this;
   }
 
+
+  // @todo Port to D7.
+  function unsetForm($form_id) {
+    unset($this->forms[$form_id]);
+    return $this;
+  }
+
   /**
    * @todo BotchaRecipebook getForms Description.
    * @return BotchaForm
    */
   function getForms() {
     $forms = array();
-    // @todo BotchaRecipebook getForms Move it to model layer.
-    if (!isset($this->forms)) {
-      $this->forms = db_select('botcha_recipebook_form', 'brf')
-        ->fields('brf', array('form_id'))
-        ->condition('rbid', $this->id)
-        ->execute()
-        ->fetchCol();
+    if (empty($this->forms)) {
+      // @todo Port this to D7.
+      $this->forms = BotchaModel::getRecipebooksForms(array(
+        'mode' => 'form',
+        'recipebooks' => $this->id,
+      ));
     }
     foreach ($this->forms as $form_id) {
       $forms[$form_id] = Botcha::getForm($form_id);
@@ -379,7 +392,10 @@ class BotchaRecipebook {
   }
 
   function apply(&$form, &$form_state) {
-    $form_state['no_cache'] = TRUE;
+    // @todo Abstract it.
+    //$form_state['no_cache'] = TRUE;
+    $form += array('#input' => TRUE); // '#input'=1 hacks FAPI to call #process handler on the form
+    $form['#process'][] = 'botcha_fprocess';
     $recipe_way = array();
     // !!~ @todo Reduce code duplication.
     // @see BotchaRecipe:applyRecipe
@@ -451,7 +467,7 @@ class BotchaRecipebookNone extends BotchaRecipebook {
     $this->title = 'None';
   }
 
-  public function isApplicable(&$form, &$form_state) {
+  public function isApplicable(&$form, $form_state) {
     // It is for admin links to be placed.
     parent::isApplicable($form, $form_state);
     return FALSE;
diff --git a/model/botcha.model.inc b/model/botcha.model.inc
index 61da32f..b4cb3d3 100644
--- a/model/botcha.model.inc
+++ b/model/botcha.model.inc
@@ -24,39 +24,27 @@ class BotchaModel {
 
   public static function getRecipebooksForms($parameters = array()) {
     // Get the value from the cache.
-    //self::$recipebooks_forms = &drupal_static('botcha_recipebooks_forms');
     //if (empty(self::$recipebooks_forms) || $parameters['reset']) {
+    // @todo Port to D7.
     $key = '';
-    $value = '';
     switch ($parameters['mode']) {
       case 'form':
-        //return self::$recipebooks_forms->fetchAllAssoc('form_id');
         $key = 'form_id';
-        $value = 'rbid';
         break;
       case 'recipebook':
       default:
-        //return self::$recipebooks_forms->fetchAllAssoc('rbid');
         $key = 'rbid';
-        $value = 'form_id';
         break;
     }
-    // @todo Remove it.
-    //$rbf = db_select('botcha_recipebook_form', 'brf')
-    //  ->fields('brf', $fields);
     $query = "SELECT * FROM {botcha_recipebook_form} brf";
     $where = '';
     if (!empty($parameters['recipebooks'])) {
       $query .= (empty($where)) ? ($where = " WHERE ") : " AND ";
       $query .= "brf.rbid IN ('" . implode("', '", (array)$parameters['recipebooks']) . "')";
-      // @todo Remove it.
-      //$rbf->condition('rbid', (array)$parameters['recipebooks'], 'IN');
     }
     if (!empty($parameters['forms'])) {
       $query .= (empty($where)) ? ($where = " WHERE ") : " AND ";
       $query .= "brf.form_id IN ('" . implode("', '", (array)$parameters['forms']) . "')";
-      // @todo Remove it.
-      //$rbf->condition('form_id', (array)$parameters['forms'], 'IN');
     }
       //self::$recipebooks_forms = $rbf;
     //}
@@ -67,19 +55,20 @@ class BotchaModel {
       $result = array();
       $result_query = db_query($query);
       while ($result_row = db_fetch_object($result_query)) {
-        $result[$result_row->$key] = $value;
+        $result[$result_row->$key] = $result_row->$key;
       }
-      // @todo Remove it.
-      //$result = $rbf->execute()->fetchCol();
     } catch (Exception $e) {
       if ($e instanceof PDOException) {
         watchdog_exception('BOTCHA', $e, 'Please perform an update via update.php or reinstall the BOTCHA module to fix the reason of this warning! %type: !message in %function (line %line of %file).', array(), WATCHDOG_WARNING);
         $result = array();
       }
     }
+    /* @todo Port to D7.
     if (count($result) == 1) {
       $result = current($result);
     }
+     *
+     */
     return $result;
   }
 
@@ -91,33 +80,24 @@ class BotchaModel {
     // Get the value from the cache.
     //self::$recipebooks_recipes = &drupal_static('botcha_recipebooks_recipes');
     //if (empty(self::$recipebooks_recipes) || $parameters['reset']) {
+    // @todo Port to D7.
     $key = '';
-    $value = '';
     switch ($parameters['mode']) {
       case 'recipe':
         $key = 'recipe_id';
-        $value = 'rbid';
         break;
       case 'recipebook':
       default:
         $key = 'rbid';
-        $value = 'recipe_id';
         break;
     }
-    // @todo Remove it.
-    //$rbr = db_select('botcha_recipebook_recipe', 'brr')
-    //  ->fields('brr', $fields);
     $query = "SELECT * FROM {botcha_recipebook_recipe} brr";
     $where = '';
     if (!empty($parameters['recipebooks'])) {
-      // @todo Remove it.
-      //$rbr->condition('rbid', (array)$parameters['recipebooks'], 'IN');
       $query .= (empty($where)) ? ($where = " WHERE ") : " AND ";
       $query .= "brr.rbid IN ('" . implode("', '", (array)$parameters['recipebooks']) . "')";
     }
     if (!empty($parameters['recipes'])) {
-      // @todo Remove it.
-      //$rbr->condition('recipe_id', (array)$parameters['recipes'], 'IN');
       $query .= (empty($where)) ? ($where = " WHERE ") : " AND ";
       $query .= "brr.recipe_id IN ('" . implode("', '", (array)$parameters['recipes']) . "')";
     }
@@ -128,10 +108,8 @@ class BotchaModel {
       $result = array();
       $result_query = db_query($query);
       while ($result_row = db_fetch_object($result_query)) {
-        $result[$result_row->$key] = $value;
+        $result[$result_row->$key] = $result_row->$key;
       }
-      // @todo Remove it.
-      //$result = $rbr->execute()->fetchCol();
     } catch (Exception $e) {
       if ($e instanceof PDOException) {
         watchdog_exception('BOTCHA', $e, 'Please perform an update via update.php or reinstall the BOTCHA module to fix the reason of this warning! %type: !message in %function (line %line of %file).', array(), WATCHDOG_WARNING);
diff --git a/model/botcha_form.model.inc b/model/botcha_form.model.inc
index dc93a98..d91bba5 100644
--- a/model/botcha_form.model.inc
+++ b/model/botcha_form.model.inc
@@ -8,14 +8,9 @@ class BotchaFormModel {
   protected static $forms;
 
   public static function getForms($parameters = array()) {
-    // @todo Remove it.
-    //$forms = db_select('botcha_form', 'bf')
-    //  ->fields('bf');
     $query = "SELECT * FROM {botcha_form} bf";
     $where = '';
     if (!empty($parameters['forms'])) {
-      // @todo Remove it.
-      //$forms->condition('id', $form_id, 'IN');
       $query .= (empty($where)) ? ($where = " WHERE ") : " AND ";
       $query .= "bf.id IN ('" . implode("', '", (array)$parameters['forms']) . "')";
     }
@@ -23,8 +18,6 @@ class BotchaFormModel {
     // reason is in that form_alter is called before performing an update.
     // @see http://drupal.org/node/1828710
     try {
-      // @todo Remove it.
-      //$result = $forms->execute()->fetchAllAssoc('id');
       $result = array();
       $result_query = db_query($query);
       while ($result_row = db_fetch_object($result_query)) {
@@ -56,41 +49,16 @@ class BotchaFormModel {
     // @see http://drupal.org/node/1828710
     try {
       // Save form.
-      // @todo Remove it.
-      /*
-      $forms = db_select('botcha_form', 'bf')
-        ->fields('bf')
-        ->condition('id', $form->id)
-        ->execute()
-        ->fetchCol();
-       *
-       */
       if (!db_result(db_query("SELECT COUNT(*) FROM {botcha_form} bf WHERE bf.id = '%s'", array($form->id)))) {
-        // @todo Remove it.
-        /*
-        db_insert('botcha_form')
-          ->fields(array('id' => $form->id))
-          ->execute();
-         *
-         */
-        update_sql("INSERT INTO {botcha_form} (id) VALUES('%s')", array($form->id));
+        db_query("INSERT INTO {botcha_form} (id) VALUES('%s')", array($form->id));
       }
       $recipebook = $form->getRecipebook();
       // Save form-recipe book relationship.
-      // @todo Remove it.
-      /*
-      db_merge('botcha_recipebook_form')
-        ->key(array('form_id' => $form->id))
-        ->fields(array(
-          'rbid' => $recipebook->id,
-        ))->execute();
-       *
-       */
-      if (!db_result(db_query("SELECT COUNT(*) FROM {botcha_recipebook_form} brf WHERE brf.form_id = '%s'", array($form->id)))) {
-        update_sql("UPDATE {botcha_recipebook_form} SET rbid = '%s' WHERE brf.form_id = '%s'", array($recipebook->id, $form->id));
+      if (db_result(db_query("SELECT COUNT(*) FROM {botcha_recipebook_form} brf WHERE brf.form_id = '%s'", array($form->id)))) {
+        db_query("UPDATE {botcha_recipebook_form} SET rbid = '%s' WHERE form_id = '%s'", array($recipebook->id, $form->id));
       }
       else {
-        update_sql("INSERT INTO {botcha_recipebook_form} (form_id, rbid) VALUES('%s', '%s')", array($form->id, $recipebook->id));
+        db_query("INSERT INTO {botcha_recipebook_form} (form_id, rbid) VALUES('%s', '%s')", array($form->id, $recipebook->id));
       }
       self::$forms[$form->id] = $form->id;
     } catch (Exception $e) {
@@ -106,21 +74,7 @@ class BotchaFormModel {
     // @see http://drupal.org/node/1828710
     try {
       // Delete all data related to this form.
-      // @todo Remove it.
-      /*
-      db_delete('botcha_recipebook_form')
-        ->condition('form_id', $form->id)
-        ->execute();
-       *
-       */
       db_query("DELETE FROM {botcha_recipebook_form} WHERE form_id = '%s'", array($form->id));
-      // @todo Remove it.
-      /*
-      db_delete('botcha_form')
-        ->condition('id', $form->id)
-        ->execute();
-       *
-       */
       db_query("DELETE FROM {botcha_form} WHERE id = '%s'", array($form->id));
       unset(self::$forms[$form->id]);
     } catch (Exception $e) {
diff --git a/model/botcha_recipe.model.inc b/model/botcha_recipe.model.inc
index 55b6193..659a1b4 100644
--- a/model/botcha_recipe.model.inc
+++ b/model/botcha_recipe.model.inc
@@ -7,18 +7,10 @@ class BotchaRecipeModel {
 
   protected static $recipes;
 
-  public static function getRecipes($parameters = NULL) {
-    // @todo Remove it.
-    /*
-    $recipes = db_select('botcha_recipe', 'br')
-      ->fields('br');
-     *
-     */
+  public static function getRecipes($parameters = array()) {
     $query = "SELECT * FROM {botcha_recipe} br";
     $where = '';
     if (!empty($parameters['recipes'])) {
-      // @todo Remove it.
-      //$recipes->condition('id', $recipe_id, 'IN');
       $query .= (empty($where)) ? ($where = " WHERE ") : " AND ";
       $query .= "br.recipe_id IN ('" . implode("', '", (array)$parameters['recipes']) . "')";
     }
@@ -26,8 +18,6 @@ class BotchaRecipeModel {
     // reason is in that form_alter is called before performing an update.
     // @see http://drupal.org/node/1828710
     try {
-      // @todo Remove it.
-      //$result = $recipes->execute()->fetchAllAssoc('id');
       $result = array();
       $result_query = db_query($query);
       while ($result_row = db_fetch_object($result_query)) {
@@ -55,23 +45,11 @@ class BotchaRecipeModel {
     // @see http://drupal.org/node/1828710
     try {
       // Save recipe to DB.
-      // @todo Remove it.
-      /*
-      db_merge('botcha_recipe')
-        ->fields(array('id', 'classname', 'title', 'description'))
-        ->values(array(
-          'id' => $this->id,
-          'classname' => $this->classname,
-          'title' => $this->title,
-          'description' => $this->description,
-        ))->execute();
-       *
-       */
-      if (!db_result(db_query("SELECT COUNT(*) FROM {botcha_recipe} br WHERE br.id = '%s'", array($recipe->id)))) {
-        update_sql("UPDATE {botcha_recipe} SET classname = '%s', title = '%s', description = '%s' WHERE br.id = '%s'", array($recipe->classname, $recipe->title, $recipe->description, $recipe->id));
+      if (db_result(db_query("SELECT COUNT(*) FROM {botcha_recipe} br WHERE br.id = '%s'", array($recipe->id)))) {
+        db_query("UPDATE {botcha_recipe} SET classname = '%s', title = '%s', description = '%s' WHERE id = '%s'", array($recipe->classname, $recipe->title, $recipe->description, $recipe->id));
       }
       else {
-        update_sql("INSERT INTO {botcha_recipe} (id, classname, title, description) VALUES('%s', '%s', '%s', '%s')", array($recipe->id, $recipe->classname, $recipe->title, $recipe->description));
+        db_query("INSERT INTO {botcha_recipe} (id, classname, title, description) VALUES('%s', '%s', '%s', '%s')", array($recipe->id, $recipe->classname, $recipe->title, $recipe->description));
       }
     } catch (Exception $e) {
       if ($e instanceof PDOException) {
diff --git a/model/botcha_recipebook.model.inc b/model/botcha_recipebook.model.inc
index 12b1650..d0b29d8 100644
--- a/model/botcha_recipebook.model.inc
+++ b/model/botcha_recipebook.model.inc
@@ -8,17 +8,9 @@ class BotchaRecipebookModel {
   protected static $recipebooks;
 
   public static function getRecipebooks($parameters = NULL) {
-    // @todo Remove it.
-    /*
-    $recipebooks = db_select('botcha_recipebook', 'brb')
-      ->fields('brb');
-     *
-     */
     $query = "SELECT * FROM {botcha_recipebook} brb";
     $where = '';
     if (!empty($parameters['recipebooks'])) {
-      // @todo Remove it.
-      //$recipebooks->condition('id', $rbid, 'IN');
       $query .= (empty($where)) ? ($where = " WHERE ") : " AND ";
       $query .= "brb.id IN ('" . implode("', '", (array)$parameters['recipebooks']) . "')";
     }
@@ -26,8 +18,6 @@ class BotchaRecipebookModel {
     // reason is in that form_alter is called before performing an update.
     // @see http://drupal.org/node/1828710
     try {
-      // @todo Remove it.
-      //$result = $recipebooks->execute()->fetchAllAssoc('id');
       $result = array();
       $result_query = db_query($query);
       while ($result_row = db_fetch_object($result_query)) {
@@ -58,95 +48,30 @@ class BotchaRecipebookModel {
     // reason is in that form_alter is called before performing an update.
     // @see http://drupal.org/node/1828710
     try {
-      // @todo Remove it.
-      /*
-      db_delete('botcha_recipebook_recipe')
-        ->condition('rbid', $recipebook->id)
-        ->execute();
-       *
-       */
       db_query("DELETE FROM {botcha_recipebook_recipe} WHERE rbid = '%s'", array($recipebook->id));
-      // @todo Remove it.
-      /*
-      db_delete('botcha_recipebook_form')
-        ->condition('rbid', $recipebook->id)
-        ->execute();
-       *
-       */
       db_query("DELETE FROM {botcha_recipebook_form} WHERE rbid = '%s'", array($recipebook->id));
       if (!($recipebook instanceof BotchaRecipebookNone)) {
-        // @todo Remove it.
-        /*
-        db_merge('botcha_recipebook')
-          ->key(array('id' => $recipebook->id))
-          ->fields(array(
-            'title' => $recipebook->title,
-            'description' => $recipebook->description,
-          ))->execute();
-         *
-         */
-        if (!db_result(db_query("SELECT COUNT(*) FROM {botcha_recipebook} brb WHERE brb.id = '%s'", array($recipebook->id)))) {
-          update_sql("UPDATE {botcha_recipebook} SET title = '%s', description = '%s' WHERE brb.id = '%s'", array($recipe->title, $recipe->description, $recipe->id));
+        if (db_result(db_query("SELECT COUNT(*) FROM {botcha_recipebook} brb WHERE brb.id = '%s'", array($recipebook->id)))) {
+          db_query("UPDATE {botcha_recipebook} SET title = '%s', description = '%s' WHERE id = '%s'", array($recipebook->title, $recipebook->description, $recipebook->id));
         }
         else {
-          update_sql("INSERT INTO {botcha_recipebook} (id, title, description) VALUES('%s', '%s', '%s')", array($recipe->id, $recipe->title, $recipe->description));
+          db_query("INSERT INTO {botcha_recipebook} (id, title, description) VALUES('%s', '%s', '%s')", array($recipebook->id, $recipebook->title, $recipebook->description));
         }
         // Save relationships between recipe book and recipes to DB.
         $recipes = $recipebook->getRecipes();
-        // @todo Remove it.
-        //$insert = db_insert('botcha_recipebook_recipe');
         $query = "INSERT INTO {botcha_recipebook_recipe} (rbid, recipe_id) VALUES('%s', '%s')";
         foreach ($recipes as $recipe) {
-          // @todo Remove it.
-          /*
-          $query = $insert->fields(array('rbid', 'recipe_id'))
-            ->values(array(
-              'rbid' => $recipebook->id,
-              'recipe_id' => $recipe->id,
-            ))->execute();
-           *
-           */
           db_query($query, array($recipebook->id, $recipe->id));
         }
         // Save relationships between recipe book and forms to DB.
         $forms = $recipebook->getForms();
         foreach ($forms as $form) {
-          // @todo Remove it.
-          /*
-          $brf = db_select('botcha_recipebook_form', 'brf')
-            ->fields('brf', array('rbid'))
-            ->condition('form_id', $form->id)
-            ->execute()
-            ->fetchCol();
-           *
-           */
-          if (!db_result(db_query("SELECT COUNT(*) FROM {botcha_recipebook_form} brf WHERE brf.form_id = '%s'", array($form->id)))) {
-            // @todo Remove it.
-            /*
-            $query = db_update('botcha_recipebook_form')
-              ->condition('form_id', $form->id)
-              ->fields(array('rbid' => $recipebook->id));
-             *
-             */
-            update_sql("UPDATE {botcha_recipebook_form} SET form_id = '%s' WHERE rbid = '%s'", array($recipebook->id));
+          if (db_result(db_query("SELECT COUNT(*) FROM {botcha_recipebook_form} brf WHERE brf.form_id = '%s'", array($form->id)))) {
+            db_query("UPDATE {botcha_recipebook_form} SET rbid = '%s' WHERE form_id = '%s'", array($recipebook->id, $form->id));
           }
           else {
-            // @todo Remove it.
-            /*
-            $query = db_insert('botcha_recipebook_form')
-              ->fields(array('form_id' => $form->id, 'rbid' => $recipebook->id));
-             *
-             */
-            update_sql("INSERT INTO {botcha_recipebook_form} (rbid, form_id) VALUES('%s', '%s')", array($recipebook->id, $form->id));
+            db_query("INSERT INTO {botcha_recipebook_form} (rbid, form_id) VALUES('%s', '%s')", array($recipebook->id, $form->id));
           }
-          // @todo Remove it.
-          //$query->execute();
-          // Strange but true: db_merge does not work here, fails with integrity constraint violation.
-          /*
-          $query->key(array('form_id', 'rbid'), array($form->id, $recipebook->id))
-            ->execute();
-           *
-           */
         }
       }
     } catch (Exception $e) {
@@ -165,29 +90,8 @@ class BotchaRecipebookModel {
     // reason is in that form_alter is called before performing an update.
     // @see http://drupal.org/node/1828710
     try {
-      // @todo Remove it.
-      /*
-      db_delete('botcha_recipebook_recipe')
-        ->condition('rbid', $recipebook->id)
-        ->execute();
-       *
-       */
       db_query("DELETE FROM {botcha_recipebook_recipe} WHERE rbid = '%s'", array($recipebook->id));
-      // @todo Remove it.
-      /*
-      db_delete('botcha_recipebook_form')
-        ->condition('rbid', $recipebook->id)
-        ->execute();
-       *
-       */
       db_query("DELETE FROM {botcha_recipebook_form} WHERE rbid = '%s'", array($recipebook->id));
-      // @todo Remove it.
-      /*
-      db_delete('botcha_recipebook')
-        ->condition('id', $recipebook->id)
-        ->execute();
-       *
-       */
       db_query("DELETE FROM {botcha_recipebook} WHERE rbid = '%s'", array($recipebook->id));
     } catch (Exception $e) {
       if ($e instanceof PDOException) {
