diff --git a/mass_contact.admin.inc b/mass_contact.admin.inc
index db22169..a6b10e3 100644
--- a/mass_contact.admin.inc
+++ b/mass_contact.admin.inc
@@ -25,21 +25,20 @@ function mass_contact_admin_categories() {
     ->orderBy('category', 'ASC')
     ->execute();
   foreach ($results as $category) {
-    $roles = array();
-
-    foreach (explode(',', $category->recipients) as $rid) {
-      $role = db_select('role', 'r')
-        ->fields('r', array('name'))
-        ->condition('rid', $rid)
-        ->execute()
-        ->fetchObject();
-      $roles[] = ($role->name);
+
+    // Execute all plugins 'mass_contact_admin_categories_callback' functions
+    // to prepare string for Recipients column.
+    ctools_include('plugins');
+    $plugins = ctools_get_plugins('mass_contact', 'groupping_method');
+    $recipients = array();
+    foreach ($plugins as $plugin) {
+      $function = ctools_plugin_get_function($plugin, 'mass_contact_admin_categories_callback');
+      $recipients[] = $function(unserialize($category->recipients));
     }
 
-    $rolenames = implode(', ', $roles);
     $rows[] = array(
       $category->category,
-      $rolenames,
+      implode('<br/>', array_filter($recipients)),
       ($category->selected ? t('Yes') : t('No')),
       l(t('edit'), 'admin/config/system/mass_contact/edit/' . $category->cid),
       l(t('delete'), 'admin/config/system/mass_contact/delete/' . $category->cid)
@@ -98,25 +97,18 @@ function mass_contact_admin_edit($form, $form_state, $cid = NULL) {
     '#required' => TRUE,
   );
 
-  // Get a list of all roles, except for the anonymous user role.
-  $allroles = db_select('role', 'r')
-    ->fields('r', array('rid', 'name'))
-    ->condition('rid', 1, '>')
-    ->orderBy('name', 'ASC')
-    ->execute();
-  foreach ($allroles as $roleobj) {
-    $onerid = $roleobj->rid;
-    $onename = $roleobj->name;
-    $rolesarray[$onerid] = $onename;
-  }
   $form['recipients'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Roles to receive email'),
-    '#options' => $rolesarray,
-    '#default_value' => explode(',', $edit['recipients']),
-    '#description' => t('These roles will be added to the mailing list. Note: if you check "authenticated users", other roles will not be added, as they will receive the email anyway.'),
+    '#tree' => TRUE,
   );
 
+  // Add form elements provided by groupping_method plugins.
+  ctools_include('plugins');
+  $plugins = ctools_get_plugins('mass_contact', 'groupping_method');
+  foreach ($plugins as $plugin_name => $plugin) {
+    $function = ctools_plugin_get_function($plugin, 'mass_contact_admin_edit');
+    $form['recipients'][$plugin_name] = $function(unserialize($edit['recipients']));
+  }
+
   $form['selected_categories'] = array(
     '#type' => 'fieldset',
     '#title' => t('Selected categories'),
@@ -158,13 +150,21 @@ function mass_contact_admin_edit($form, $form_state, $cid = NULL) {
  *   A keyed array containing the current state of the form.
  */
 function mass_contact_admin_edit_validate($form, &$form_state) {
-  $recipients = $form_state['values']['recipients'];
-  foreach ($recipients as $checkr) {
-    if ($checkr > 1) {
-      return;
+  // Execute validation callbacks for each plugin.
+  ctools_include('plugins');
+  $plugins = ctools_get_plugins('mass_contact', 'groupping_method');
+  $values_empty = TRUE;
+  foreach ($plugins as $plugin) {
+    $function = ctools_plugin_get_function($plugin, 'mass_contact_admin_edit_validate');
+    if ($values_empty) {
+      $values_empty = $function($form, $form_state);
     }
   }
-  form_set_error('recipients', t('You must check one or more recipients.'));
+  // If all validation callbacks return TRUE that means that user
+  // hasn't selected any selection rules.
+  if ($values_empty) {
+    form_set_error('recipients', t('You must check one or more recipients.'));
+  }
 } // End of mass_contact_admin_edit_validate().
 
 /**
@@ -183,26 +183,16 @@ function mass_contact_admin_edit_submit($form, &$form_state) {
       ->execute();
   }
 
-  // Remove 0s for unselected roles, convert to csv.
-  $recipients = $form_state['values']['recipients'];
-
-  // If all authenticated users are already added, remove all roles.
-  if ($recipients[2] == 2) {
-    foreach ($recipients as $checkr) {
-      if ($checkr > 2) {
-        $recipients[$checkr] = 0;
-      }
-    }
-  }
-
-  // Remove roles that were not selected.
-  foreach ($recipients as $recip) {
-    if ($recip != 0) {
-      $newformrec[] = $recip;
-    }
+  // Execute submit callbacks for each plugin. Collect recipients.
+  ctools_include('plugins');
+  $plugins = ctools_get_plugins('mass_contact', 'groupping_method');
+  $recipients = array();
+  foreach ($plugins as $plugin_name => $plugin) {
+    $function = ctools_plugin_get_function($plugin, 'mass_contact_admin_edit_submit');
+    $recipients[$plugin_name] = $function($form, $form_state);
   }
 
-  $form_state['values']['recipients'] = implode(',', $newformrec);
+  $recipients = serialize($recipients);
 
   if (!isset($form_state['values']['reply'])) {
     $form_state['values']['reply'] = '';
@@ -211,30 +201,21 @@ function mass_contact_admin_edit_submit($form, &$form_state) {
     $form_state['values']['weight'] = 0;
   }
 
+  $record = array(
+    'category' => $form_state['values']['category'],
+    'recipients' => $recipients,
+    'reply' => $form_state['values']['reply'],
+    'weight' => $form_state['values']['weight'],
+    'selected' => $form_state['values']['selected'],
+  );
   if (arg(4) == 'add') {
-    db_insert('mass_contact')
-      ->fields(array(
-        'category' => $form_state['values']['category'],
-        'recipients' => $form_state['values']['recipients'],
-        'reply' => $form_state['values']['reply'],
-        'weight' => $form_state['values']['weight'],
-        'selected' => $form_state['values']['selected'],
-      ))
-      ->execute();
+    drupal_write_record('mass_contact', $record);
     drupal_set_message(t('Category %category has been added.', array('%category' => $form_state['values']['category'])));
     watchdog('mass_contact', 'Mass Contact form: category %category added.', array('%category' => $form_state['values']['category']), WATCHDOG_NOTICE, l(t('view'), 'admin/config/system/mass_contact'));
   }
   else {
-    db_update('mass_contact')
-      ->fields(array(
-        'category' => $form_state['values']['category'],
-        'recipients' => $form_state['values']['recipients'],
-        'reply' => $form_state['values']['reply'],
-        'weight' => $form_state['values']['weight'],
-        'selected' => $form_state['values']['selected'],
-      ))
-      ->condition('cid', $form_state['values']['cid'])
-      ->execute();
+    $record['cid'] = $form_state['values']['cid'];
+    drupal_write_record('mass_contact', $record, array('cid'));
     drupal_set_message(t('Category %category has been updated.', array('%category' => $form_state['values']['category'])));
     watchdog('mass_contact', 'Mass Contact form: category %category updated.', array('%category' => $form_state['values']['category']), WATCHDOG_NOTICE, l(t('view'), 'admin/config/system/mass_contact'));
   }
diff --git a/mass_contact.info b/mass_contact.info
index 4315c53..9f13c86 100644
--- a/mass_contact.info
+++ b/mass_contact.info
@@ -6,3 +6,5 @@ core = 7.x
 php = 4.3.5
 package = "Mail"
 configure = admin/config/system/mass_contact
+
+dependencies[] = ctools
diff --git a/mass_contact.install b/mass_contact.install
index 718bd77..24aec0c 100644
--- a/mass_contact.install
+++ b/mass_contact.install
@@ -29,7 +29,7 @@ function mass_contact_schema() {
         'default' => '',
       ),
       'recipients' => array(
-        'description' => 'A list of the users to receive the message.',
+        'description' => 'Field that has information about users to receive the message.',
         'type' => 'text',
         'size' => 'big',
         'not null' => TRUE,
@@ -344,3 +344,14 @@ function mass_contact_update_7100(&$sandbox) {
   variable_del('mass_contact_html_format_override');
   variable_del('mass_contact_nodecc_d_override');
 } // End of mass_contact_update_7100().
+
+/**
+ * Move mass_contact.recipients database field to serialized data.
+ */
+function mass_contact_update_7101(&$sandbox) {
+  $records = db_query('SELECT cid, recipients FROM {mass_contact}');
+  foreach ($records as $record) {
+    $recipients = serialize(array('mass_contact_role' => explode(',', $record->recipients)));
+    db_query('UPDATE {mass_contact} SET recipients = :recipients WHERE cid = :cid', array(':cid' => $record->cid, ':recipients' => $recipients));
+  }
+}
\ No newline at end of file
diff --git a/mass_contact.module b/mass_contact.module
index 57edbbf..9276d15 100644
--- a/mass_contact.module
+++ b/mass_contact.module
@@ -359,3 +359,22 @@ function mass_contact_mail($key, &$message, $params) {
     $message['body'][] = $params['body'];
   }
 } // End of mass_contact_mail().
+
+/**
+ * Implements hook_ctools_plugin_type().
+ */
+function mass_contact_ctools_plugin_type() {
+  return array(
+    'groupping_method' => array(),
+  );
+}
+
+/**
+ * Implements hook_ctools_plugin_directory().
+ */
+function mass_contact_ctools_plugin_directory($module, $plugin) {
+  if (($module == 'mass_contact') && ($plugin == 'groupping_method')) {
+    return 'plugins';
+  }
+}
+
diff --git a/mass_contact.page.inc b/mass_contact.page.inc
index 0d30dab..a30dec5 100644
--- a/mass_contact.page.inc
+++ b/mass_contact.page.inc
@@ -1017,10 +1017,23 @@ function _mass_contact_create_recipient_list($category, $respect_opt_outs) {
   $opt_out_setting = variable_get('mass_contact_optout_d', 0);
   global $user;
 
-  // Get the user IDs for all the users in all the roles included in the
-  // category.
-  $query = "SELECT u.name, u.mail, u.data FROM {users} u LEFT JOIN {users_roles} ur ON u.uid = ur.uid WHERE ur.rid IN(:ur_rids) AND u.status <> 0";
-  $results = db_query($query, array(':ur_rids' => $category->recipients));
+  // Collect user id's by executing all plugin's
+  // create_recipient_list_callback functions.
+  ctools_include('plugins');
+  $plugins = ctools_get_plugins('mass_contact', 'groupping_method');
+  $user_uids = array();
+  foreach ($plugins as $plugin) {
+    $function = ctools_plugin_get_function($plugin, 'create_recipient_list_callback');
+    $user_uids += $function(unserialize($category->recipients));
+  }
+
+  $uids = array_unique(array_values($user_uids));
+  if (empty($uids)) {
+    return array();
+  }
+
+  $query = "SELECT u.name, u.mail, u.data FROM {users} u WHERE uid IN (:uids) AND u.status <> 0";
+  $results = db_query($query, array(':uids' => $uids));
   foreach ($results as $account) {
     $account_data = unserialize($account->data);
 
diff --git a/plugins/mass_contact_role.inc b/plugins/mass_contact_role.inc
new file mode 100644
index 0000000..eff73bb
--- /dev/null
+++ b/plugins/mass_contact_role.inc
@@ -0,0 +1,134 @@
+<?php
+
+/**
+ * @file mass_contact plugin type groupping_method.
+ *
+ * Select users by their role.
+ */
+
+$plugin = array(
+  // Function executed to retrieve list of users by category settings.
+  // In case of roles we receive unserialized settings with selected roles.
+  'create_recipient_list_callback' => 'mass_contact_role_create_recipient_list',
+
+  // Function to prepare Category description for Recipients column
+  // on categories list admin page.
+  'mass_contact_admin_categories_callback' => 'mass_contact_role_admin_categories',
+
+  // Next three callbacks used to maintain form of add/edit category.
+  'mass_contact_admin_edit' => 'mass_contact_role_admin_edit',
+  'mass_contact_admin_edit_validate' => 'mass_contact_role_admin_edit_validate',
+  'mass_contact_admin_edit_submit' => 'mass_contact_role_admin_edit_submit',
+);
+
+/**
+ * Callback to retrieve users by roles.
+ *
+ * Get the user IDs for all the users in all the roles included in the
+ * category.
+ */
+function mass_contact_role_create_recipient_list($recipients) {
+  $groups = array();
+  if (isset($recipients['mass_contact_role'])) {
+    $groups = $recipients['mass_contact_role'];
+  }
+
+  $query = "SELECT u.uid FROM {users} u LEFT JOIN {users_roles} ur ON u.uid = ur.uid WHERE ur.rid IN (:ur_rids)";
+  $result = db_query($query, array(':ur_rids' => $groups))->fetchAllAssoc('uid', PDO::FETCH_ASSOC);
+  $uids = array();
+  foreach ($result as $record) {
+    $uids[] = $record['uid'];
+  }
+  return $uids;
+}
+
+/**
+ * Callback to prepare role names for admin/config/system/mass_contact page
+ * column Recipients.
+ */
+function mass_contact_role_admin_categories($recipients) {
+  $groups = array();
+  if (isset($recipients['mass_contact_role']) && !empty($recipients['mass_contact_role'])) {
+    $groups = $recipients['mass_contact_role'];
+  }
+  else {
+    return;
+  }
+
+  $result = db_query('SELECT name FROM {role} WHERE rid IN (:rids)', array(':rids' => $groups));
+
+  $role_names = array();
+  foreach ($result as $record) {
+    $role_names[] = $record->name;
+  }
+
+  return t('Roles: %roles', array('%roles' => implode(', ', $role_names)));
+}
+
+/**
+ * Form element for Category add/edit page.
+ *
+ * @param array $roles
+ * @return type
+ */
+function mass_contact_role_admin_edit($recipients) {
+  $roles = array();
+  if (isset($recipients['mass_contact_role']) && !empty($recipients['mass_contact_role'])) {
+    $roles = $recipients['mass_contact_role'];
+  }
+
+  // Get a list of all roles, except for the anonymous user role.
+  $allroles = db_select('role', 'r')
+    ->fields('r', array('rid', 'name'))
+    ->condition('rid', 1, '>')
+    ->orderBy('name', 'ASC')
+    ->execute();
+  foreach ($allroles as $roleobj) {
+    $onerid = $roleobj->rid;
+    $onename = $roleobj->name;
+    $rolesarray[$onerid] = $onename;
+  }
+  $form_element = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Roles to receive email'),
+    '#options' => $rolesarray,
+    '#default_value' => $roles,
+    '#description' => t('These roles will be added to the mailing list. Note: if you check "authenticated users", other roles will not be added, as they will receive the email anyway.'),
+  );
+
+  return $form_element;
+}
+
+/**
+ * Add/edit validation callback. Set form error and return whether selection
+ * is empty or not.
+ *
+ * @param array $form
+ * @param array $form_state
+ * @return bool
+ */
+function mass_contact_role_admin_edit_validate($form, &$form_state) {
+  $selected_roles = array_filter($form_state['values']['recipients']['mass_contact_role']);
+  return empty($selected_roles);
+}
+
+/**
+ * Add/edit form submit callback. Should return piece of data that will be
+ * saved to mass_contact table in recepients field.
+ *
+ * @param type $form
+ * @param type $form_state
+ */
+function mass_contact_role_admin_edit_submit($form, &$form_state) {
+  $roles = $form_state['values']['recipients']['mass_contact_role'];
+
+  // If all authenticated users are already added,
+  // simply return authenticated users rid.
+  if ($roles[2] == 2) {
+    return array(2);
+  }
+  // Remove roles that were not selected.
+  $roles = array_filter($roles);
+
+  return $roles;
+}
diff --git a/plugins/mass_contact_taxonomy.inc b/plugins/mass_contact_taxonomy.inc
new file mode 100644
index 0000000..534dfe3
--- /dev/null
+++ b/plugins/mass_contact_taxonomy.inc
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * @file mass_contact plugin type groupping_method.
+ *
+ * Select users by taxonomy terms.
+ */
+
+$plugin = array(
+  'create_recipient_list_callback' => 'mass_contact_taxonomy_create_recipient_list',
+  'mass_contact_admin_categories_callback' => 'mass_contact_taxonomy_admin_categories',
+  'mass_contact_admin_edit' => 'mass_contact_taxonomy_admin_edit',
+  'mass_contact_admin_edit_validate' => 'mass_contact_taxonomy_admin_edit_validate',
+  'mass_contact_admin_edit_submit' => 'mass_contact_taxonomy_admin_edit_submit',
+);
+
+/**
+ * Callback to retrieve users by taxonomy terms.
+ *
+ * Get the user IDs for all the users that have specified taxonomy terms
+ * attached to user object.
+ */
+function mass_contact_taxonomy_create_recipient_list($recipients) {
+  $tids = array();
+  if (!isset($recipients['mass_contact_taxonomy']) || empty($recipients['mass_contact_taxonomy'])) {
+    return array();
+  }
+  $tids = $recipients['mass_contact_taxonomy'];
+
+  // Query to select users by term.
+  // Check all fields of user entity and if field is taxonomy term reference,
+  // add condition.
+  $query = new EntityFieldQuery();
+  $user_fields = field_info_instances('user');
+  foreach ($user_fields['user'] as $user_field_instance) {
+    $field = field_info_field($user_field_instance['field_name']);
+    if ($field['module'] == 'taxonomy') {
+      $query->fieldCondition($field['field_name'], 'tid', $tids, 'IN');
+    }
+  }
+  $result = $query->execute();
+  // Collect uids.
+  $uids = array();
+  if (isset($result['user'])) {
+    $uids = array_keys($result['user']);
+  }
+
+  return $uids;
+}
+
+/**
+ * Callback to prepare taxonomy term names for admin/config/system/mass_contact
+ * page column Recipients.
+ */
+function mass_contact_taxonomy_admin_categories($recipients) {
+  $tids = array();
+  if (!isset($recipients['mass_contact_taxonomy']) || empty($recipients['mass_contact_taxonomy'])) {
+    return;
+  }
+  $tids = $recipients['mass_contact_taxonomy'];
+
+  $terms = taxonomy_term_load_multiple($tids);
+  if (empty($terms)) {
+    return;
+  }
+
+  $term_names = array();
+  foreach ($terms as $term) {
+    $term_names[] = $term->name;
+  }
+
+  return t('Taxonomy terms: %terms', array('%terms' => implode(', ', $term_names)));
+}
+
+/**
+ * Form element for Category add/edit page.
+ *
+ * @param array $terms
+ * @return type
+ */
+function mass_contact_taxonomy_admin_edit($recipients) {
+  // Prepare array of field_name => vocabulary of taxonomy fields.
+  $user_fields = field_info_instances('user');
+  $taxonomy_fields = array();
+  foreach ($user_fields['user'] as $user_field_instance) {
+    $field = field_info_field($user_field_instance['field_name']);
+    if ($field['module'] == 'taxonomy') {
+      $taxonomy_fields[$field['field_name']] = $field['settings']['allowed_values'][0]['vocabulary'];
+    }
+  }
+
+  $form_element = array();
+  foreach ($taxonomy_fields as $field_name => $vocabulary_name) {
+    $vocabulary = taxonomy_vocabulary_machine_name_load($vocabulary_name);
+    $terms = taxonomy_get_tree($vocabulary->vid);
+    $options = array();
+    foreach ($terms as $term) {
+      $options[$term->tid] = check_plain($term->name);
+    }
+    $default_value = array();
+    if (isset($recipients['mass_contact_taxonomy'])) {
+      $default_value = $recipients['mass_contact_taxonomy'];
+    }
+    $form_element[$field_name] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Taxonomy vocabulary: %vocabulary', array('%vocabulary' => $vocabulary->name)),
+      '#options' => $options,
+      '#default_value' => $default_value,
+    );
+  }
+  return $form_element;
+}
+
+/**
+ * Add/edit validation callback. Set form error and return whether selection
+ * is empty or not.
+ *
+ * @param array $form
+ * @param array $form_state
+ * @return bool
+ */
+function mass_contact_taxonomy_admin_edit_validate($form, &$form_state) {
+  foreach ($form_state['values']['recipients']['mass_contact_taxonomy'] as $term_values) {
+    $term_values_filtered = array_filter($term_values);
+    if (!empty($term_values_filtered)) {
+      return FALSE;
+    }
+  }
+  return TRUE;
+}
+
+/**
+ * Add/edit form submit callback. Should return piece of data that will be
+ * saved to mass_contact table in recepients field.
+ *
+ * @param type $form
+ * @param type $form_state
+ */
+function mass_contact_taxonomy_admin_edit_submit($form, &$form_state) {
+  $terms = array();
+  foreach ($form_state['values']['recipients']['mass_contact_taxonomy'] as $term_values) {
+    $terms += array_filter($term_values);
+  }
+  return $terms;
+}
\ No newline at end of file
