You are here

simplenews.module in Simplenews 5

File

simplenews.module
View source
<?php

/**
 * Implementation of hook_node_info().
 */
function simplenews_node_info() {
  return array(
    'simplenews' => array(
      'name' => t('Newsletter issue'),
      'module' => 'simplenews',
      'description' => t('Create a newsletter issue to be sent to subscribed e-mail addresses.'),
    ),
  );
}

/**
 * Implementation of hook_perm().
 */
function simplenews_perm() {
  return array(
    'view links in block',
    'create newsletter',
    'edit own newsletter',
    'edit any newsletter',
    'administer newsletters',
    'send newsletter',
    'subscribe to newsletters',
  );
}

/**
 * Implementation of hook_init().
 */
function simplenews_init() {

  // only bother when we are not serving a cached page. check for which function that only exists afterwards
  if (function_exists('drupal_set_content')) {
    if (module_exists('views')) {
      include drupal_get_path('module', 'simplenews') . '/simplenews_views.inc';
    }
  }
}

/**
 * Implementation of hook_access().
 */
function simplenews_access($op, $node) {
  global $user;
  if ($op == 'create') {
    if (user_access('create newsletter') || user_access('administer newsletters')) {
      return TRUE;
    }
  }
  if ($op == 'update' || $op == 'delete') {
    if (user_access('administer newsletters')) {
      return TRUE;
    }
    elseif (user_access('edit any newsletter')) {
      return TRUE;
    }
    elseif (user_access('edit own newsletter') && $user->uid == $node->uid) {
      return TRUE;
    }
  }
}

/**
 * Implementation of hook_menu().
 */
function simplenews_menu($may_cache) {
  $items = array();
  $administer = user_access('administer newsletters');
  if ($may_cache) {
    $items[] = array(
      'path' => 'node/add/simplenews',
      'title' => t('Newsletter issue'),
      'access' => user_access('create newsletter'),
    );
    $items[] = array(
      'path' => 'admin/content/newsletters',
      'title' => t('Newsletters'),
      'type' => MENU_NORMAL_ITEM,
      'description' => t('Manage newsletters, subscriptions, and configuration options.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'simplenews_admin_news',
      ),
      'access' => $administer,
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/sent',
      'title' => t('Sent issues'),
      'access' => $administer,
      'type' => MENU_DEFAULT_LOCAL_TASK,
      'weight' => -10,
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'simplenews_admin_news',
      ),
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/notsent',
      'title' => t('Drafts'),
      'access' => $administer,
      'type' => MENU_LOCAL_TASK,
      'weight' => -9,
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'simplenews_admin_news',
        'notsent',
      ),
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/types',
      'title' => t('Newsletters'),
      'access' => $administer,
      'type' => MENU_LOCAL_TASK,
      'weight' => -8,
      'callback' => 'simplenews_types_overview',
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/types/edit',
      'title' => t('Newsletters'),
      'access' => $administer,
      'type' => MENU_CALLBACK,
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'simplenews_admin_types_form',
      ),
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/types/delete',
      'title' => t('Newsletters'),
      'access' => $administer,
      'type' => MENU_CALLBACK,
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'simplenews_admin_types_delete',
      ),
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/types/list',
      'title' => t('List newsletters'),
      'access' => $administer,
      'type' => MENU_DEFAULT_LOCAL_TASK,
      'weight' => -10,
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/types/add',
      'title' => t('Add newsletter'),
      'access' => $administer,
      'type' => MENU_LOCAL_TASK,
      'weight' => -9,
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'simplenews_admin_types_form',
      ),
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/subscriptions/delete',
      'title' => t('Delete'),
      'type' => MENU_CALLBACK,
      'callback' => 'drupal_get_form',
      'callback arguments' => 'simplenews_subscription_multiple_delete_confirm',
      'access' => $administer,
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/users',
      'title' => t('Subscriptions'),
      'access' => $administer,
      'type' => MENU_LOCAL_TASK,
      'weight' => -7,
      'callback' => 'simplenews_subscription_admin',
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/users/edit',
      'title' => t('Subscriptions'),
      'access' => $administer,
      'type' => MENU_CALLBACK,
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'simplenews_admin_users_form',
      ),
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/users/delete',
      'title' => t('Subscriptions'),
      'access' => $administer,
      'type' => MENU_CALLBACK,
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'simplenews_admin_users_delete',
      ),
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/users/list',
      'title' => t('List subscriptions'),
      'access' => $administer,
      'type' => MENU_DEFAULT_LOCAL_TASK,
      'weight' => -10,
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/users/import',
      'title' => t('Import subscriptions'),
      'access' => $administer,
      'type' => MENU_LOCAL_TASK,
      'weight' => -9,
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'simplenews_subscription_list_add',
      ),
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/users/export',
      'title' => t('Export subscriptions'),
      'access' => $administer,
      'type' => MENU_LOCAL_TASK,
      'weight' => -8,
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'simplenews_subscription_list_export',
      ),
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/settings',
      'title' => t('Settings'),
      'access' => $administer,
      'type' => MENU_LOCAL_TASK,
      'weight' => -6,
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'simplenews_admin_settings',
      ),
    );
    $items[] = array(
      'path' => 'admin/content/newsletters/settings/general',
      'title' => t('General'),
      'access' => $administer,
      'type' => MENU_DEFAULT_LOCAL_TASK,
      'weight' => -10,
    );
    $items[] = array(
      'path' => 'newsletter/confirm',
      'title' => t('Confirm newsletter subscriptions'),
      'access' => user_access('subscribe to newsletters'),
      'callback' => 'simplenews_confirm_subscription',
      'type' => MENU_CALLBACK,
    );
    $items[] = array(
      'path' => 'newsletter/subscriptions',
      'title' => t('Manage newsletter subscriptions'),
      'access' => user_access('subscribe to newsletters'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'simplenews_subscription_manager_form',
      ),
      'type' => MENU_CALLBACK,
    );
  }
  else {
    drupal_add_css(drupal_get_path('module', 'simplenews') . '/simplenews.css', 'module', 'all', TRUE);
    if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'newsletters' && arg(3) == 'settings') {
      $tree = taxonomy_get_tree(variable_get('simplenews_vid', ''));
      if ($tree) {
        $weight = -9;
        foreach ($tree as $newsletter) {
          $items[] = array(
            'path' => 'admin/content/newsletters/settings/' . $newsletter->tid,
            'title' => $newsletter->name,
            'access' => $administer,
            'type' => MENU_LOCAL_TASK,
            'weight' => $weight++,
          );
        }
      }
    }
  }
  return $items;
}

/**
 * Implementation of hook_form().
 */
function simplenews_form(&$node) {
  $type = node_get_types('type', $node);
  global $user;
  if ($type->has_title) {
    $form['title'] = array(
      '#type' => 'textfield',
      '#title' => check_plain($type->title_label),
      '#default_value' => $node->title,
      '#size' => 60,
      '#maxlength' => 128,
      '#required' => TRUE,
    );
  }
  if ($type->has_body) {
    $form['body_filter']['body'] = array(
      '#type' => 'textarea',
      '#title' => check_plain($type->body_label),
      '#default_value' => $node->body,
      '#rows' => 20,
      '#required' => $type->min_word_count > 0,
      '#description' => t('This will be the body of your newsletter. Available variables are:') . ' %site ' . t('(the name of your website),') . ' %uri ' . t('(a link to your homepage),') . ' %uri_brief ' . t('(homepage link without the http://),') . ' %mymail ' . t('(your e-mail address),') . ' %date ' . t('(today\'s date),') . ' %login_uri ' . t('(link to login page).'),
    );
    $form['body_filter']['format'] = filter_form($node->format);
  }
  if (!($sel1 = $node->s_format)) {
    $sel1 = variable_get('simplenews_format', 'plain');
  }
  if (!($sel2 = $node->priority)) {
    $sel2 = variable_get('simplenews_priority', 0);
  }
  if (!($sel3 = $node->receipt)) {
    $sel3 = variable_get('simplenews_receipt', 0);
  }
  $form['sending_options'] = array(
    '#type' => 'fieldset',
    '#title' => t('Newsletter sending options'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $form['sending_options']['s_format'] = array(
    '#type' => 'select',
    '#title' => t('Format'),
    '#default_value' => $sel1,
    '#options' => _simplenews_format_options(),
  );
  $form['sending_options']['priority'] = array(
    '#type' => 'select',
    '#title' => t('Priority'),
    '#default_value' => $sel2,
    '#options' => array(
      0 => t('none'),
      1 => t('highest'),
      2 => t('high'),
      3 => t('normal'),
      4 => t('low'),
      5 => t('lowest'),
    ),
  );
  $form['sending_options']['receipt'] = array(
    '#type' => 'checkbox',
    '#title' => t('Request receipt'),
    '#return_value' => 1,
    '#default_value' => $sel3,
  );
  if ($node->s_status == 0) {
    if (user_access('send newsletter')) {
      $options[0] = t("Don't send now");
      $options[2] = t('Send one test newsletter to the test address');
      $options[1] = t('Send newsletter');
      $form['sending_options']['send'] = array(
        '#type' => 'radios',
        '#title' => t('Sending'),
        '#default_value' => $node->send ? $node->send : variable_get('simplenews_send', 0),
        '#options' => $options,
      );
    }
    else {
      $options[0] = t("Don't send now");
      $options[2] = t('Send one test newsletter to the test address');
      $form['sending_options']['send'] = array(
        '#type' => 'radios',
        '#title' => t('Sending'),
        '#default_value' => $node->send ? $node->send : 0,
        '#options' => $options,
        '#description' => t('You have no privileges to send this newsletter'),
      );
    }
    if (variable_get('simplenews_test_address_override', 0)) {
      $form['sending_options']['test_address'] = array(
        '#type' => 'textfield',
        '#title' => t('Test e-mail addresses'),
        '#default_value' => $node->test_address ? $node->test_address : variable_get('simplenews_test_address', ''),
        '#size' => 60,
        '#maxlength' => 128,
        '#description' => t('Supply a comma-separated list of e-mail addresses to be used as test addresses.'),
      );
    }
  }
  else {
    $atts = array(
      'disabled' => 'disabled',
    );
    $form['sending_options']['none'] = array(
      '#type' => 'checkbox',
      '#title' => t('This newsletter has been sent'),
      '#return_value' => 0,
      '#attributes' => array(
        'checked' => 'checked',
        'disabled' => 'disabled',
      ),
    );
  }
  $form['s_status'] = array(
    '#type' => 'hidden',
    '#value' => $node->s_status ? $node->s_status : 0,
  );
  return $form;
}

/**
 * Implementation of hook_validate().
 */
function simplenews_validate($node) {
  global $valid_mails;
  if ($node->send == 1) {
    if (!isset($node->taxonomy)) {
      $names = node_get_types('names', $node);
      form_set_error('', t('You should select content type %content_type in !newsletter_vocabulary before you can send this newsletter.', array(
        '%content_type' => $names[$node->type],
        '!newsletter_vocabulary' => l(t('Newsletter vocabulary'), 'admin/content/taxonomy/edit/vocabulary/' . variable_get('simplenews_vid', '')),
      )));
    }
    elseif (!simplenews_validate_taxonomy($node->taxonomy)) {
      form_set_error('taxonomy', t('You should select a newsletter before you can send this newsletter.'));
    }
  }
  elseif ($node->send == 2) {
    if (variable_get('simplenews_test_address_override', 0)) {
      $mails = explode(',', $node->test_address);
    }
    else {
      $mails = explode(',', variable_get('simplenews_test_address', ''));
    }
    foreach ($mails as $mail) {
      $mail = trim($mail);
      if ($mail == '') {
        form_set_error('', t('Cannot send test newsletter: no valid test e-mail address specified.'));
      }
      elseif (!valid_email_address($mail)) {
        form_set_error('', t('Cannot send test newsletter to %mail: e-mail address invalid.', array(
          '%mail' => $mail,
        )));
      }
      else {
        $valid_mails[] = $mail;
      }
    }
  }
}

/**
 * Implementation of hook_submit().
 */
function simplenews_submit(&$node) {
  global $valid_mails;
  if ($node->send == 2 && $valid_mails) {
    $node->test_address = $valid_mails;
  }
}
function simplenews_validate_taxonomy($taxonomy) {
  $vid = variable_get('simplenews_vid', '');
  $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $vid);
  while ($tid = db_fetch_object($result)) {
    $tids[] = $tid->tid;
  }
  if (isset($tids) && !empty($taxonomy)) {
    $taxes = array();
    foreach ($taxonomy as $tax) {
      $taxes[] = $tax;
    }
    $selected_terms = array_intersect($tids, $taxes);
    return empty($selected_terms) ? FALSE : $selected_terms;
  }
  return FALSE;
}

/**
 * Implementation of hook_cron().
 */
function simplenews_cron() {
  _simplenews_send(FALSE);
}

/**
 * Implementation of hook_insert().
 * Saves extra node content to module database table.
 */
function simplenews_insert($node) {
  $term = simplenews_validate_taxonomy($node->taxonomy);
  $tid = is_array($term) ? array_values($term) : FALSE;
  $node->simplenews_tid = $tid ? $tid[0] : 0;

  //tid is also saved in this table since it is needed by _simplenews_send(), and the term_node table is

  //only updated after the execution of simplenews_insert(). It cannot be passed by a variable

  //since simplenews_cron() also calls _simplenews_send().
  $s_status = $node->send == 1 && user_access('send newsletter') ? 1 : 0;
  db_query("INSERT INTO {simplenews_newsletters} (nid, vid, tid, s_status, s_format, priority, receipt)\n\t\t  VALUES (%d, %d, %d, %d, '%s', %d, %d)", $node->nid, $node->vid, $node->simplenews_tid, $s_status, $node->s_format, $node->priority, $node->receipt);
}

/**
 * Implementation of hook_update().
 */
function simplenews_update($node) {
  $term = simplenews_validate_taxonomy($node->taxonomy);
  $tid = is_array($term) ? array_values($term) : FALSE;
  $node->simplenews_tid = $tid ? $tid[0] : 0;
  if ($node->send == 1 && user_access('send newsletter')) {
    db_query("UPDATE {simplenews_newsletters} SET vid = %d, tid = %d, s_status = %d, s_format = '%s', priority = %d, receipt = %d\n    WHERE nid = %d", $node->vid, $node->simplenews_tid, 1, $node->s_format, $node->priority, $node->receipt, $node->nid);
  }
  else {
    db_query("UPDATE {simplenews_newsletters} SET tid = %d, s_format = '%s', priority = %d, receipt = %d\n    WHERE nid = %d", $node->simplenews_tid, $node->s_format, $node->priority, $node->receipt, $node->nid);
  }
}

/**
 * Implementation of hook_nodeapi().
 */
function simplenews_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  if ($node->type == 'simplenews' && ($op == 'update' || $op == 'insert')) {
    if ($node->send == 1) {
      _simplenews_send(TRUE);
      drupal_set_message(t('Newsletter %newsletter is being sent', array(
        '%newsletter' => $node->title,
      )));
    }
    elseif ($node->send == 2) {
      simplenews_send_test($node);
    }
  }
}

/**
 * Implementation of hook_delete().
 */
function simplenews_delete($node) {
  $result = db_query('DELETE FROM {simplenews_newsletters} WHERE nid = %d', $node->nid);
  if ($result) {
    drupal_set_message(t('Newsletter %title was successfully deleted.', array(
      '%title' => $node->title,
    )));
  }
}

/**
 * Implementation of hook_load().
 */
function simplenews_load($node) {
  $newsletter = db_fetch_object(db_query('SELECT * FROM {simplenews_newsletters} WHERE nid = %d', $node->nid));

  // Newsletter vid is stored in simplenews->vid.
  // This will prevent overwrite of node->vid.
  // A better solution would be a node->simplenews with all newsletter data.
  $newsletter->simplenews_vid = $newsletter->vid;
  unset($newsletter->vid);
  return $newsletter;
}

/**
 * Implementation of hook_taxonomy().
 *
 * Deletes subscriptions to term when term is deleted, and cleans the blocks
 * table.
 */
function simplenews_taxonomy($op, $type, $array = NULL) {
  if ($type == 'term' && $op == 'delete' && $array['vid'] == variable_get('simplenews_vid', '')) {
    db_query('DELETE FROM {simplenews_snid_tid} WHERE tid = %d', $array['tid']);
    db_query("DELETE FROM {blocks} WHERE module = '%s' AND delta = '%s'", 'simplenews', 'newsletter-' . $array['tid']);
    drupal_set_message(t('Deleted all subscriptions to newsletter %newsletter.', array(
      '%newsletter' => $array['name'],
    )));
  }
  elseif ($op == 'delete' && $type == 'vocabulary' && $array['vid'] == variable_get('simplenews_vid', '')) {
    variable_del('simplenews_vid');
  }
}

/**
 * Implementation of hook_view().
 */
function simplenews_view(&$node, $teaser = FALSE) {
  $node = simplenews_replace_vars($node, TRUE);
  $node = node_prepare($node, $teaser);
  return $node;
}

/**
 * Implementation of hook_form_alter().
 */
function simplenews_form_alter($form_id, &$form) {
  $vid = variable_get('simplenews_vid', '');

  // Newsletter vocabulary form
  if ($form_id == 'taxonomy_form_vocabulary' && isset($form['vid']) && $form['vid']['#value'] == $vid) {

    // Hide critical options from forum vocabulary.
    $form['help_forum_vocab'] = array(
      '#value' => t('This is the designated simplenews vocabulary. This vocabulary can not be deleted.'),
      '#weight' => -1,
    );
    $form['nodes']['#required'] = TRUE;
    $form['nodes']['#description'] = t('Select content type(s) to be used as newsletter');
    $form['multiple'] = array(
      '#type' => 'value',
      '#value' => FALSE,
    );

    // Prevent the vocabulary gets deleted
    unset($form['delete']);
  }
}

/**
 * Implementation of hook_user().
 *
 * Checks whether an email address is subscribed to the newsletter when a new
 * user signs up. If so, changes uid from 0 to the new uid in
 * simplenews_subscriptions so that the user's subscription status is known when
 * he logs in.
 */
function simplenews_user($op, &$edit, &$account, $category = NULL) {
  switch ($op) {
    case 'insert':
      if ($edit['mail']) {
        $query = "SELECT snid FROM {simplenews_subscriptions} WHERE mail = '%s'";
        if ($result = db_fetch_object(db_query($query, $edit['mail']))) {
          db_query("UPDATE {simplenews_subscriptions} SET uid = %d WHERE snid = %d", $edit['uid'], $result->snid);
        }
      }
      break;
    case 'update':
      if ($category == 'account' && $edit['mail']) {
        $query = "SELECT snid FROM {simplenews_subscriptions} WHERE uid = %d";
        if ($result = db_fetch_object(db_query($query, $account->uid))) {
          db_query("DELETE FROM {simplenews_subscriptions} WHERE mail = '%s' AND uid = %d", $edit['mail'], 0);
          db_query("UPDATE {simplenews_subscriptions} SET mail = '%s' WHERE snid = %d", $edit['mail'], $result->snid);
        }
        else {
          $query = "SELECT snid FROM {simplenews_subscriptions} WHERE mail = '%s'";
          if ($result = db_fetch_object(db_query($query, $edit['mail']))) {
            db_query("UPDATE {simplenews_subscriptions} SET uid = %d WHERE snid = %d", $account->uid, $result->snid);
          }
        }
      }

      // Activate/deactivate subscription when account is blocked/unblocked
      if ($category == 'account' && isset($edit['status'])) {
        if (variable_get('simplenews_sync_account', FALSE)) {
          db_query("UPDATE {simplenews_subscriptions} SET a_status = %d WHERE uid = %d", $edit['status'], $account->uid);
        }
      }
      if ($category == 'newsletter' && user_access('subscribe to newsletters')) {
        foreach ($edit['newsletters'] as $tid => $checked) {
          if ($checked) {
            simplenews_subscribe_user($account->mail, $tid, FALSE);
          }
          else {
            simplenews_unsubscribe_user($account->mail, $tid, FALSE);
          }
        }
      }
      break;
    case 'delete':
      if (variable_get('simplenews_sync_account', FALSE)) {

        // Delete subscription and all newsletter subscriptions.
        // We get the snid straight from the database because user_load()
        // can not be used in this stage.
        $snid = db_result(db_query("SELECT s.snid FROM {simplenews_subscriptions} s WHERE s.uid = %d", $account->uid));
        db_query('DELETE FROM {simplenews_snid_tid} WHERE snid = %d', $snid);
        db_query('DELETE FROM {simplenews_subscriptions} WHERE snid = %d', $snid);
      }
      else {

        // Only remove uid from subscription data when account is removed
        db_query("UPDATE {simplenews_subscriptions} SET uid = 0 WHERE uid = %d", $account->uid);
      }
      break;
    case 'form':
      if ($category == 'newsletter' && user_access('subscribe to newsletters')) {
        $subscription = simplenews_get_user_subscription($account->mail);
        $form = _simplenews_subscription_manager_form($subscription);
        $form['subscriptions']['#title'] = t('Current newsletter subscriptions');
        unset($form['update'], $form['subscriptions']['mail']);
        return $form;
      }
      break;
    case 'categories':
      if (user_access('subscribe to newsletters')) {
        $output[] = array(
          'name' => 'newsletter',
          'title' => t('My newsletters'),
          'weight' => 10,
        );
      }
      return $output;
    case 'view':
      global $user;
      if ($user->uid == $account->uid || user_access('administer users')) {
        $items = array();
        $tree = taxonomy_get_tree(variable_get('simplenews_vid', ''));
        foreach ($tree as $newsletter) {
          if (db_num_rows(db_query('SELECT s.uid FROM {simplenews_subscriptions} s INNER JOIN {simplenews_snid_tid} t ON s.snid = t.snid WHERE s.uid = %d AND t.tid = %d', $account->uid, $newsletter->tid))) {
            $subscriptions[] = l($newsletter->name, 'taxonomy/term/' . $newsletter->tid);
          }
        }
        if ($subscriptions) {
          $subscriptions = implode(', ', $subscriptions);
        }
        else {
          $subscriptions = t('Currently no subscriptions');
        }

        // When a user has no permission to subscribe and is not subscribed
        // we do not display the 'no subscriptions' message.
        if (user_access('subscribe to newsletters') || $subscriptions != t('Currently no subscriptions')) {
          $items[] = array(
            'class' => 'item',
            'title' => t('Current subscriptions'),
            'value' => $subscriptions,
          );
        }
        if (user_access('subscribe to newsletters')) {
          $items[] = array(
            'class' => 'item',
            'title' => t('Manage subscriptions'),
            'value' => l(t('My newsletters'), 'user/' . $account->uid . '/edit/newsletter'),
          );
        }
        return array(
          t('Newsletters') => $items,
        );
      }
      break;
  }
}

/**
 * Implementation of hook_block().
 */
function simplenews_block($op = 'list', $delta = 0, $edit = array()) {
  list($type, $tid) = explode('-', $delta);
  switch ($op) {
    case 'list':
      $blocks = array();
      foreach (taxonomy_get_tree(variable_get('simplenews_vid', '')) as $newsletter) {
        $blocks['newsletter-' . $newsletter->tid] = array(
          'info' => t('Newsletter: @title', array(
            '@title' => $newsletter->name,
          )),
        );
      }
      return $blocks;
    case 'configure':
      $form['simplenews_block_' . $delta]['simplenews_block_m_status_' . $tid] = array(
        '#type' => 'checkbox',
        '#title' => t('Display block message'),
        '#return_value' => 1,
        '#default_value' => variable_get('simplenews_block_m_status_' . $tid, 1),
      );
      $form['simplenews_block_' . $delta]['simplenews_block_m_' . $tid] = array(
        '#type' => 'textfield',
        '#title' => t('Block message'),
        '#size' => 60,
        '#maxlength' => 128,
        '#default_value' => variable_get('simplenews_block_m_' . $tid, t('Stay informed on our latest news!')),
      );
      $form['simplenews_block_' . $delta]['simplenews_block_f_' . $tid] = array(
        '#type' => 'checkbox',
        '#title' => t('Display subscription form'),
        '#return_value' => 1,
        '#description' => t('If selected a subscription form is displayed, if not selected a link to the subscription page is displayed.'),
        '#default_value' => variable_get('simplenews_block_f_' . $tid, 1),
      );
      $form['simplenews_block_' . $delta]['simplenews_block_l_' . $tid] = array(
        '#type' => 'checkbox',
        '#title' => t('Display link to previous issues'),
        '#return_value' => 1,
        '#default_value' => variable_get('simplenews_block_l_' . $tid, 1),
        '#description' => t('Links (to previous issues, previous issues and RSS-feed) are only displayed to users who have "view links in block" privileges.'),
      );
      $form['simplenews_block_' . $delta]['simplenews_block_i_status_' . $tid] = array(
        '#type' => 'checkbox',
        '#title' => t('Display previous issues'),
        '#return_value' => 1,
        '#default_value' => variable_get('simplenews_block_i_status_' . $tid, 0),
      );
      $form['simplenews_block_' . $delta]['simplenews_block_i_' . $tid] = array(
        '#type' => 'select',
        '#title' => t('Number of issues to display'),
        '#options' => drupal_map_assoc(array(
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8,
          9,
          10,
        )),
        '#default_value' => variable_get('simplenews_block_i_' . $tid, 5),
      );
      $form['simplenews_block_' . $delta]['simplenews_block_r_' . $tid] = array(
        '#type' => 'checkbox',
        '#title' => t('Display RSS-feed icon'),
        '#return_value' => 1,
        '#default_value' => variable_get('simplenews_block_r_' . $tid, 1),
      );
      return $form;
    case 'save':
      variable_set('simplenews_block_m_status_' . $tid, $edit['simplenews_block_m_status_' . $tid]);
      variable_set('simplenews_block_m_' . $tid, $edit['simplenews_block_m_' . $tid]);
      variable_set('simplenews_block_f_' . $tid, $edit['simplenews_block_f_' . $tid]);
      variable_set('simplenews_block_l_' . $tid, $edit['simplenews_block_l_' . $tid]);
      variable_set('simplenews_block_i_status_' . $tid, $edit['simplenews_block_i_status_' . $tid]);
      variable_set('simplenews_block_i_' . $tid, $edit['simplenews_block_i_' . $tid]);
      variable_set('simplenews_block_r_' . $tid, $edit['simplenews_block_r_' . $tid]);
      break;
    case 'view':
      if ($type == 'newsletter') {
        if ($newsletter = taxonomy_get_term($tid)) {
          $block['subject'] = check_plain($newsletter->name);
          $block['content'] = theme('simplenews_block', _simplenews_block($newsletter->tid));
        }
      }
      return $block;
  }
}

/**
 * Helper function: generate block content before theming
 * Collects data and applies access restriction
 */
function _simplenews_block($tid) {
  global $user;
  $block = array();
  $recent = simplenews_recent_newsletters($tid, variable_get('simplenews_block_i_' . $tid, 5));
  $block['subscribed'] = simplenews_user_is_subscribed($user->mail, $tid) == TRUE;
  $block['user'] = !empty($user->uid);
  $block['tid'] = $tid;
  $block['message'] = check_plain(variable_get('simplenews_block_m_' . $tid, t('Stay informed on our latest news!')));
  if (user_access('subscribe to newsletters')) {
    $block['form'] = drupal_get_form('simplenews_block_form_' . $tid);
    $block['subscription-link'] = l(t('Manage my subscriptions'), 'newsletter/subscriptions');
  }
  if (user_access('view links in block') || user_access('administer newsletters')) {
    $block['newsletter-link'] = l(t('Previous issues'), 'taxonomy/term/' . $tid);
    $block['itemlist'] = theme('item_list', $recent, t('Previous issues'), 'ul');
    $block['rssfeed'] = theme('feed_icon', url('taxonomy/term/' . $tid . '/0/feed'));
  }
  return $block;
}

/**
 * Simplenews themed block
 *   Access restrictions have been applied.
 *   $block contains all available data. See: _simplenews_block()
 */
function theme_simplenews_block($block) {
  $output = '';
  if (variable_get('simplenews_block_m_status_' . $block['tid'], 1) && isset($block['message'])) {
    $output .= "<p>" . $block['message'] . "</p>\n";
  }
  if (variable_get('simplenews_block_f_' . $block['tid'], 1)) {
    $output .= $block['form'];
  }
  elseif (isset($block['subscription-link'])) {
    $output .= "<p>" . $block['subscription-link'] . "</p>\n";
  }
  if (variable_get('simplenews_block_l_' . $block['tid'], 1) && isset($block['newsletter-link'])) {
    $output .= '<div class="issues-link">' . $block['newsletter-link'] . "</div>\n";
  }
  if (variable_get('simplenews_block_i_status_' . $block['tid'], 0) && isset($block['itemlist'])) {
    $output .= '<div class="issues-list">' . $block['itemlist'] . "</div>\n";
  }
  if (variable_get('simplenews_block_r_' . $block['tid'], 1)) {
    $output .= $block['rssfeed'] . "\n";
  }
  return $output;
}

/**
 * Implementation of hook_forms
 *
 * All form blocks are build using simplenews_block_form().
 * hook_forms() is required to provide unique form id for each block form.
 */
function simplenews_forms() {
  $forms = array();
  if ($tree = taxonomy_get_tree(variable_get('simplenews_vid', ''))) {
    foreach ($tree as $newsletter) {
      $forms['simplenews_block_form_' . $newsletter->tid] = array(
        'callback' => 'simplenews_block_form',
        'callback arguments' => array(
          $newsletter->tid,
        ),
      );
    }
  }
  return $forms;
}

/**
 * Helper function for API functions; loads a user or creates a dummy anonymous
 * user with uid = 0 and $mail equal to the input param.
 */
function _simplenews_user_load($mail) {
  $account = user_load(array(
    'mail' => $mail,
  ));
  if ($account === FALSE) {

    // Construct anonymous user since we don't have a user that matches that e-amil.
    $account = new stdClass();
    $account->uid = 0;
    $account->mail = strtolower($mail);
  }
  return $account;
}

/**
 * API function; subscribes a user to a newsletter.
 *
 * @param $mail
 *   The e-mail address to subscribe to the newsletter.
 * @param $tid
 *   The term ID of the newsletter.
 * @param $confirm
 *   Whether we should send a confirmation e-mail and hold off adding this user
 *   to the newsletter until he or she clicks the confirm link in the e-mail.
 */
function simplenews_subscribe_user($mail, $tid, $confirm = TRUE) {

  //Prevent mismatches from accidental capitals in mail address
  $mail = strtolower($mail);
  $subscription = simplenews_get_user_subscription($mail);

  // If user is not subscribed to ANY newsletter, add basic info first.
  if (!$subscription) {
    $account = _simplenews_user_load($mail);
    db_query("INSERT INTO {simplenews_subscriptions} (mail, uid, a_status) VALUES ('%s', %d, 1)", $mail, $account->uid);
    $subscription = simplenews_get_user_subscription($mail);
  }
  $newsletter = taxonomy_get_term($tid);
  if ($confirm) {

    // Send confirmation e-mail to user to complete subscription or to tell
    // them that he or she is already subscribed.
    simplenews_mail_confirm($mail, $newsletter, $subscription ? $subscription->snid : NULL, 'subscribe');
  }
  elseif (!isset($subscription->tids[$tid])) {

    // Then, add user to newsletter relationship if not already subscribed.
    db_query("INSERT INTO {simplenews_snid_tid} (snid, tid) VALUES (%d, %d)", $subscription->snid, $tid);
  }
  return TRUE;
}

/**
 * API function; unsubscribes a user from a newsletter.
 *
 * @param $mail
 *   The e-mail address to unsubscribe from the newsletter.
 * @param $tid
 *   The term ID of the newsletter.
 * @param $confirm
 *   Whether we should send a confirmation e-mail and hold off removing this
 *   user from the newsletter until he clicks the confirm link in the e-mail.
 */
function simplenews_unsubscribe_user($mail, $tid, $confirm = TRUE) {

  //Prevent mismatches from accidental capitals in mail address
  $mail = strtolower($mail);
  $subscription = simplenews_get_user_subscription($mail);
  if (!($newsletter = taxonomy_get_term($tid))) {
    watchdog('newsletter', t('Could not load newsletter term ID %id', array(
      '%id' => $tid,
    )));
    return FALSE;
  }
  if ($confirm) {

    // Send confirmation e-mail to user to complete unsubscription or to tell
    // them that he or she is not subscribed.
    simplenews_mail_confirm($mail, $newsletter, $subscription ? $subscription->snid : NULL, 'unsubscribe');
  }
  elseif (isset($subscription->tids[$tid])) {

    // If we're not confirming first, just remove the user from the newsletter.
    db_query('DELETE FROM {simplenews_snid_tid} WHERE snid = %d AND tid = %d', $subscription->snid, $tid);

    // Clean up simplenews_subscriptions if no more newsletter subscriptions.
    if (!db_num_rows(db_query("SELECT tid FROM {simplenews_snid_tid} t WHERE t.snid = %d", $subscription->snid))) {
      db_query('DELETE FROM {simplenews_subscriptions} WHERE snid = %d', $subscription->snid);
    }
  }
  return TRUE;
}

/**
 * API function; returns if the user's e-mail address is subscribed to the given
 * newsletter.
 */
function simplenews_user_is_subscribed($mail, $tid) {
  $account = _simplenews_user_load($mail);
  return db_num_rows(db_query("SELECT * FROM {simplenews_subscriptions} s INNER JOIN {simplenews_snid_tid} t ON s.snid = t.snid WHERE s.mail = '%s' AND s.uid = %d AND t.tid = %d", $account->mail, $account->uid, $tid)) ? TRUE : FALSE;
}

/**
 * API function; returns the subscription for the given e-mail address.
 */
function simplenews_get_user_subscription($mail) {
  $account = _simplenews_user_load($mail);
  $snid = db_result(db_query("SELECT snid FROM {simplenews_subscriptions} s WHERE s.mail = '%s' AND s.uid = %d", $account->mail, $account->uid));
  return simplenews_get_subscription($snid);
}

/**
 * API function; returns the subscription for the given subscription ID.
 */
function simplenews_get_subscription($snid) {
  $subscription = db_fetch_object(db_query("SELECT * FROM {simplenews_subscriptions} s WHERE s.snid = %d", $snid));
  if ($subscription) {
    $result = db_query("SELECT tid FROM {simplenews_snid_tid} t WHERE t.snid = %d", $subscription->snid);
    $subscription->tids = array();
    while ($newsletter = db_fetch_object($result)) {
      $subscription->tids[$newsletter->tid] = $newsletter->tid;
    }
    return $subscription;
  }
  else {
    return FALSE;
  }
}
function simplenews_handle_messages($one, $two_or_more, $type = 'status') {
  if (isset($_SESSION['messages'][$type])) {
    $msg = $_SESSION['messages'][$type];
  }
  else {
    $msg = array();
  }
  $key = array_search($one, $msg);
  if ($key || $key === 0) {
    $_SESSION['messages'][$type][$key] = $two_or_more;
  }
  elseif (!in_array($two_or_more, $msg)) {
    drupal_set_message($one, $type);
  }
}

/**
 * Generates the subscription form for users.
 */
function simplenews_subscription_manager_form($snid = NULL) {
  global $user;
  if (isset($snid)) {
    $subscription = simplenews_get_subscription($snid);

    // If we couldn't load subscription set defaults.
    if (!$subscription) {
      $subscription = new stdClass();
      $subscription->tids = array();
    }
  }
  else {
    $subscription = simplenews_get_user_subscription($user->mail);

    // If current user is not subscribed to any newsletter set current user data.
    if ($user->uid && !$subscription) {
      $subscription = new stdClass();
      $subscription->tids = array();
      $subscription->uid = $user->uid;
      $subscription->mail = $user->mail;
    }
  }

  // If non-admin is trying to edit someone else's subscription, access denied.
  if ($user->uid && $user->uid != $subscription->uid && !user_access('subscribe to newsletters')) {
    drupal_access_denied();
    return;
  }
  $form = _simplenews_subscription_manager_form($subscription);
  return $form;
}

/**
 * Helper function to build subscription manager form, also used in user edit.
 */
function _simplenews_subscription_manager_form($subscription) {
  global $user;
  $form = array();
  $form['#base'] = 'simplenews_subscription_manager_form';
  $options = array();
  foreach (taxonomy_get_tree(variable_get('simplenews_vid', '')) as $newsletter) {
    $options[$newsletter->tid] = check_plain($newsletter->name);
  }
  $form['subscriptions'] = array(
    '#type' => 'fieldset',
    '#description' => t('Select the newsletter(s) to which you want to subscribe or unsubscribe.'),
  );
  $form['subscriptions']['newsletters'] = array(
    '#type' => 'checkboxes',
    '#options' => $options,
    '#default_value' => $subscription->tids,
  );

  // If current user is an authenticated, just display e-mail. Anonymous users
  // see an e-mail box and will receive confirmations
  if ($user->uid) {
    $form['subscriptions']['#title'] = t('Subscriptions for %mail', array(
      '%mail' => $subscription->mail,
    ));
    $form['subscriptions']['mail'] = array(
      '#type' => 'value',
      '#value' => $subscription->mail,
    );
    $form['update'] = array(
      '#type' => 'submit',
      '#value' => t('Update'),
      '#weight' => 20,
    );
  }
  else {
    $form['subscriptions']['#title'] = t('Manage your newsletter subscriptions');
    $form['subscriptions']['mail'] = array(
      '#type' => 'textfield',
      '#title' => t('E-mail'),
      '#size' => 20,
      '#maxlength' => 128,
      '#weight' => 10,
      '#required' => TRUE,
    );
    $form['subscribe'] = array(
      '#type' => 'submit',
      '#value' => t('Subscribe'),
      '#weight' => 20,
    );
    $form['unsubscribe'] = array(
      '#type' => 'submit',
      '#value' => t('Unsubscribe'),
      '#weight' => 30,
    );
  }
  return $form;
}

/**
 * Forms API callback; validates the settings form.
 */
function simplenews_subscription_manager_form_validate($form_id, $form_values) {
  $valid_email = valid_email_address($form_values['mail']);
  if (!$valid_email) {
    form_set_error('mail', t('The e-mail address you supplied is not valid.'));
  }
  $checked_newsletters = array_filter($form_values['newsletters']);
  if (!count($checked_newsletters) && !simplenews_get_user_subscription($form_values['mail'])) {
    form_set_error('newsletters', t('You must select at least one newsletter.'));
  }
}

/**
 * Forms API callback; submit handler for subscription form.
 */
function simplenews_subscription_manager_form_submit($form_id, $form_values) {
  switch ($form_values['op']) {
    case t('Update'):
      foreach ($form_values['newsletters'] as $tid => $checked) {
        if ($checked) {
          simplenews_subscribe_user($form_values['mail'], $tid, FALSE);
        }
        else {
          simplenews_unsubscribe_user($form_values['mail'], $tid, FALSE);
        }
      }
      drupal_set_message(t('The newsletter subscriptions for %mail have been updated.', array(
        '%mail' => $form_values['mail'],
      )));
      break;
    case t('Subscribe'):
      foreach ($form_values['newsletters'] as $tid => $checked) {
        if ($checked) {
          simplenews_subscribe_user($form_values['mail'], $tid);
        }
      }
      drupal_set_message(t('You will receive a confirmation e-mail shortly containing further instructions on how to complete your subscription.'));
      break;
    case t('Unsubscribe'):
      foreach ($form_values['newsletters'] as $tid => $checked) {
        if ($checked) {
          simplenews_unsubscribe_user($form_values['mail'], $tid);
        }
      }
      drupal_set_message(t('You will receive a confirmation e-mail shortly containing further instructions on how to complete the unsubscription process.'));
      break;
  }

  // Return to home page unless we set the destination back to the admin page.
  return '';
}
function simplenews_recent_newsletters($tid, $count = 5, $title = NULL) {
  $result = db_query_range(db_rewrite_sql('SELECT n.nid, n.title, sn.s_status FROM {node} n INNER JOIN {term_node} t ON n.nid = t.nid INNER JOIN {simplenews_newsletters} sn ON n.nid = sn.nid WHERE (t.tid = %d AND n.status = 1) ORDER BY n.created DESC'), $tid, 0, $count);
  $titles = array();
  while ($item = db_fetch_object($result)) {
    $titles[$item->nid]['data'] = l($item->title, 'node/' . $item->nid);
    $titles[$item->nid]['class'] = $item->s_status == 0 ? 'newsletter-created' : 'newsletter-send';
  }
  return $titles;
}

/**
 * Show block subscription form.
 */
function simplenews_block_form($tid) {
  global $user;
  $form = array();
  if ($user->uid) {
    if (simplenews_user_is_subscribed($user->mail, $tid)) {
      $submit_text = t('Unsubscribe');
      $form['action'] = array(
        '#type' => 'value',
        '#value' => 'unsubscribe',
      );
    }
    else {
      $submit_text = t('Subscribe');
      $form['action'] = array(
        '#type' => 'value',
        '#value' => 'subscribe',
      );
    }
    $form['display_mail'] = array(
      '#title' => t('User'),
      '#value' => check_plain($user->name),
      '#prefix' => '<div class="user-name">',
      '#suffix' => '</div>',
    );
    $form['mail'] = array(
      '#type' => 'value',
      '#value' => $user->mail,
    );
  }
  else {
    $form['mail'] = array(
      '#type' => 'textfield',
      '#title' => t('E-mail'),
      '#size' => 20,
      '#maxlength' => 128,
      '#required' => TRUE,
    );
    $form['action'] = array(
      '#type' => 'radios',
      '#default_value' => 'subscribe',
      '#options' => array(
        'subscribe' => t('Subscribe'),
        'unsubscribe' => t('Unsubscribe'),
      ),
    );
  }

  // All block forms use the same validate and submit function.
  // 'tid' carries the tid for processing of the right newsletter issue term.
  $form['tid'] = array(
    '#type' => 'value',
    '#value' => $tid,
  );
  $form['#base'] = 'simplenews_block_form';
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => isset($submit_text) ? $submit_text : t('Submit'),
  );
  return $form;
}

/**
 * Forms API callback; handles block form (un)subscribe validation.
 */
function simplenews_block_form_validate($form_id, $form_values) {
  if (!valid_email_address($form_values['mail'])) {
    form_set_error('mail', t("The e-mail address you supplied is not valid."));
  }
}

/**
 * Forms API callback; handles block form (un)subscribe submissions.
 */
function simplenews_block_form_submit($form_id, $form_values) {
  global $user;
  $account = _simplenews_user_load($form_values['mail']);

  // If e-mail belongs to the current registered user, don't send confirmation.
  $confirm = $account->uid && $account->uid == $user->uid ? FALSE : TRUE;
  switch ($form_values['action']) {
    case 'subscribe':
      simplenews_subscribe_user($form_values['mail'], $form_values['tid'], $confirm);
      if ($confirm) {
        drupal_set_message(t('You will receive a confirmation e-mail shortly containing further instructions on how to complete your subscription.'));
      }
      else {
        drupal_set_message(t('You have been successfully subscribed.'));
      }
      break;
    case 'unsubscribe':
      simplenews_unsubscribe_user($form_values['mail'], $form_values['tid'], $confirm);
      if ($confirm) {
        drupal_set_message(t('You will receive a confirmation e-mail shortly containing further instructions on how to complete the unsubscription process.'));
      }
      else {
        drupal_set_message(t('You have been successfully unsubscribed.'));
      }
      break;
  }
}

/**
* Prepare node for sending
*/
function simplenews_node_prepare($nid, $vid, $tid) {

  // node_load() sometimes returns empty nodes. The cause of this problem is yet unknown.
  if (!($node = node_load($nid, $vid, TRUE))) {
    return FALSE;
  }
  $node = simplenews_replace_vars($node, FALSE);

  // To play well with other modules that add content to the node,
  // simplenews_node_prepare() must mimic node_view() as far as possible.
  // Following is adapted from node_view().
  $node = node_build_content($node, FALSE, TRUE);
  $content = drupal_render($node->content);
  $node->body = $content;
  unset($node->teaser);
  node_invoke_nodeapi($node, 'alter', FALSE, TRUE);
  $node = theme('simplenews_newsletter', $node, $tid);

  // TODO: Probably should refactor the whole processing, but check here for
  // mimemail and don't mess with body if we're gonna pass it to mimemail().
  if ($node->s_format == 'plain' || !module_exists('mimemail')) {
    $node->body = simplenews_html_to_text($node->body, variable_get('simplenews_hyperlinks_' . $tid, 1));
  }
  simplenews_set_from($node, $tid);
  return $node;
}

/**
 * Helper function to set from name and e-mail for a mail object.
 *
 * Newsletter specific from name and address are set by admin. They default
 * to Simplenews general from name and address. Which on their turn default
 * to site name and from email address.
 *
 * @param object &$mail simplenews mail object.
 * @param integer $tid newsletter term id
 */
function simplenews_set_from(&$mail, $tid = NULL) {
  $address_default = variable_get('site_mail', ini_get('sendmail_from'));
  $name_default = variable_get('site_name', 'drupal');
  if (isset($tid)) {
    $mail->from_address = variable_get('simplenews_from_address_' . $tid, variable_get('simplenews_from_address', $address_default));
    $mail->from_name = variable_get('simplenews_from_name_' . $tid, variable_get('simplenews_from_name', $name_default));
  }
  else {
    $mail->from_address = variable_get('simplenews_from_address', $address_default);
    $mail->from_name = variable_get('simplenews_from_name', $name_default);
  }
}

/**
* Send the newsletter
*/
function _simplenews_send($timer = FALSE) {
  $max_time = variable_get('simplenews_time', 5);
  if ($timer && $max_time == 0) {
    return;
  }
  if ($max_time == 0) {
    $max_time = 1;
  }
  $max_time = $max_time - 0.5;
  $start_time = simplenews_time();
  if (!$timer) {
    $throttle = variable_get('simplenews_throttle', 20);
    static $counter = 0;
  }
  $result = db_query(db_rewrite_sql('SELECT n.nid, s.vid, s.tid, n.created FROM {node} n INNER JOIN {simplenews_newsletters} s ON n.nid = s.nid WHERE s.s_status = %d ORDER BY n.created ASC'), 1);
  while ($nid = db_fetch_object($result)) {
    $term = taxonomy_get_term($nid->tid);

    // To prevent empty emails from being send, we interrupt the send action and try again next time.
    if (!($node = simplenews_node_prepare($nid->nid, $nid->vid, $nid->tid))) {
      watchdog('newsletter', t('Empty newsletter node loaded (nid = @nid; vid = @vid). Please report in the simplenews issue queue.', array(
        '@nid' => $nid->nid,
        '@vid' => $nid->vid,
      )), WATCHDOG_ERROR);
      return;
    }
    $result2 = db_query('SELECT s.mail, s.snid FROM {simplenews_subscriptions} s INNER JOIN {simplenews_snid_tid} t ON s.snid = t.snid WHERE s.s_status = %d AND s.a_status = %d AND t.tid = %d ORDER BY s.snid ASC', 0, 1, $nid->tid);
    while ($mail = db_fetch_object($result2)) {
      $hash = _simplenews_generate_hash($mail->mail, $mail->snid, $nid->tid);

      // Add themable footer to message as this changes per user.
      $user_node = drupal_clone($node);
      $user_node->to = $mail->mail;
      $user_node = theme('simplenews_newsletter_footer', $user_node, $hash);
      $counter++;
      if (simplenews_mail_send($user_node)) {

        // TODO: This looks like it may choke if you were sending multiple
        // newsletters through cron. Should move s_status to snid_tid table
        // or somewhere else to see which newsletter has been sent.
        db_query('UPDATE {simplenews_subscriptions} SET s_status = %d WHERE snid = %d', 1, $mail->snid);

        // don't send mails too fast, servers may choke. Wait for 10 ms.
        usleep(10000);
      }
      else {
        watchdog('newsletter', t('Newsletter %title could not be sent to %email.', array(
          '%title' => $user_node->title,
          '%email' => $mail->mail,
        )), WATCHDOG_ERROR);
      }
      if ($timer) {
        $int_time = simplenews_time();
      }
      else {
        if ($counter < $throttle) {
          $int_time = $start_time;
        }
        else {
          return;
        }
      }
      if (!($int_time - $start_time < $max_time)) {
        return;
      }
    }
    db_query('UPDATE {simplenews_subscriptions} SET s_status = %d', 0);
    db_query('UPDATE {simplenews_newsletters} SET s_status = %d WHERE nid = %d', 2, $node->nid);
    if ($timer) {
      $int_time = simplenews_time();
    }
    else {
      $int_time = $start_time;
    }
    if (!($int_time - $start_time < $max_time)) {
      return;
    }
  }
}

/**
* Send a test newsletter
*/
function simplenews_send_test($input) {
  $tid = db_result(db_query('SELECT tid FROM {simplenews_newsletters} WHERE nid = %d', $input->nid));
  $tid = $tid ? $tid : FALSE;
  $node = simplenews_node_prepare($input->nid, NULL, $tid);
  $term = $tid ? taxonomy_get_term($tid) : FALSE;
  $name = $term ? $term->name : 'Unassigned newsletter';
  $node->body .= "\n\n-- \n" . t('Footer will be appended here');
  $recipients = $input->test_address;
  foreach ($recipients as $to) {
    $node->to = $to;
    if (simplenews_mail_send($node)) {
      drupal_set_message(t('Test newsletter sent to %recipient.', array(
        '%recipient' => $to,
      )));
    }
  }
}

/**
* Send confirmation email
*/
function simplenews_mail_confirm($email, $newsletter, $snid = NULL, $op = NULL) {
  if (isset($snid) && isset($newsletter->tid)) {
    $hash = _simplenews_generate_hash($email, $snid, $newsletter->tid);
  }
  else {
    $hash = NULL;
  }
  $mail = theme('simplenews_newsletter_confirmation', $email, $newsletter, $snid, $op, $hash);
  if (simplenews_mail_send($mail)) {
    watchdog('newsletter', t('Sent confirmation e-mail to %mail.', array(
      '%mail' => $email,
    )));
  }
  else {
    watchdog('newsletter', t('Sending of confirmation e-mail to %mail failed.', array(
      '%mail' => $email,
    )), WATCHDOG_ERROR);
  }
}

/**
 * Mail engine to send newsletter. If you want to send HTML newsletters you need
 * to plug in an extra module
 *
 * @param $mail
 *   An object with at least $mail->to, $mail->subject, and $mail->message.
 */
function simplenews_mail_send($mail) {
  $from_email = isset($mail->from_address) ? $mail->from_address : variable_get('site_mail', ini_get('sendmail_from'));
  $from = isset($mail->from_name) ? '"' . mime_header_encode($mail->from_name) . '" <' . $from_email . '>' : $from_email;
  $headers = array(
    'From' => $from,
    'Reply-To' => $from_email,
    'X-Mailer' => 'Drupal',
    'Return-Path' => $from_email,
    'Errors-To' => $from_email,
  );

  // If receipt is requested, add headers.
  if ($mail->receipt) {
    $headers['Disposition-Notification-To'] = $from_email;
    $headers['X-Confirm-Reading-To'] = $from_email;
  }

  // Add priority if set.
  switch ($mail->priority) {
    case 1:
      $headers['Priority'] = 'High';
      $headers['X-Priority'] = '1';
      $headers['X-MSMail-Priority'] = 'Highest';
      break;
    case 2:
      $headers['Priority'] = 'urgent';
      $headers['X-Priority'] = '2';
      $headers['X-MSMail-Priority'] = 'High';
      break;
    case 3:
      $headers['Priority'] = 'normal';
      $headers['X-Priority'] = '3';
      $headers['X-MSMail-Priority'] = 'Normal';
      break;
    case 4:
      $headers['Priority'] = 'non-urgent';
      $headers['X-Priority'] = '4';
      $headers['X-MSMail-Priority'] = 'Low';
      break;
    case 5:
      $headers['Priority'] = 'non-urgent';
      $headers['X-Priority'] = '5';
      $headers['X-MSMail-Priority'] = 'Lowest';
      break;
  }

  // If subject is not set, default to title.
  if (!isset($mail->subject)) {
    $mail->subject = $mail->title;
  }
  if (module_exists('mimemail')) {
    if ($mail->s_format == 'plain') {
      $plain_text_only = TRUE;
      $plain_text_body = $mail->body;
    }
    else {
      $plain_text_only = FALSE;
      $plain_text_body = NULL;
    }
    if (variable_get('simplenews_debug', FALSE)) {
      watchdog('simplenews', t('Outgoing email via mimemail.<br />Subject: %subject<br />Recipient: %to', array(
        '%to' => $mail->to,
        '%subject' => $mail->subject,
      )), WATCHDOG_NOTICE);
    }
    return mimemail($from, $mail->to, $mail->subject, $mail->body, $plain_text_only, $headers, $plain_text_body, array(), 'simplenews-send-mail');
  }
  else {
    if (variable_get('simplenews_debug', FALSE)) {
      watchdog('simplenews', t('Outgoing email.<br />Subject: %subject<br />Recipient: %to', array(
        '%to' => $mail->to,
        '%subject' => $mail->subject,
      )), WATCHDOG_NOTICE);
    }
    return drupal_mail('simplenews-send-mail', $mail->to, $mail->subject, $mail->body, $from_email, $headers);
  }
}

/**
 * Convert html text to plain text.
 *
 * Conversion of html tags, special characters and anchor and image tags.
 * URLs in anchor and image tags are converted to absolute URLs and, depending
 * on admin setting, placed in-line or at the bottom of the email.
 *
 * @param string $txt
 *    html text string
 * @param boolean $inline
 *    TRUE = anchor and image tags are converted in-line;
 *    FALSE = anchor and image tags are converted and placed at the bottom of the message.
 * @return string
 *    converted plain text string
 */
function simplenews_html_to_text($txt, $inline) {
  $pattern = '@(<a[^>]+?href="(.\\S+?)"[^>]*>(.+?)</a>)|(<img[^>]+?src="([^"]*)"[^>]*?>)@eis';
  if ($inline) {
    $txt = preg_replace($pattern, "_simplenews_mail_uri('\\2\\5', '\\3')", $txt);
  }
  else {

    // Converted 'a' and 'img' URLs are converted by and stored in _simplenews_mail_urls().
    $txt = preg_replace($pattern, "'\\3 ['. _simplenews_mail_urls('\\2\\5') .']'", $txt);

    // Retreive array of URLs from _simplenews_mail_urls() and format them
    // as a list at the end of the message.
    $urls = _simplenews_mail_urls();
    if (count($urls)) {
      $txt .= "\n";
      $i = 0;
      for ($max = count($urls); $i < $max; $i++) {
        $txt .= '[' . ($i + 1) . '] ' . $urls[$i] . "\n";
      }
    }

    // Empty the the stored list of URLs.
    _simplenews_mail_urls(0, TRUE);
  }

  // some basic html to text conversion
  $txt = preg_replace(_simplenews_define_search(), _simplenews_define_replace(), $txt);
  $txt = preg_replace("/\n\\s+\n/", "\n\n", $txt);
  $txt = strip_tags($txt);
  $txt = decode_entities($txt);
  return wordwrap($txt, 80);
}

/**
 * Format URL and link text for in-line display in plain text message.
 *
 * @param string $href
 *    Absolute URL
 * @param string $link
 *    Link text
 * @retun string
 *    Formatted URL with link text. Format 'url_text [ link_text ]'
 */
function _simplenews_mail_uri($href, $link) {
  $href = _simplenews_mail_url($href);
  if ($href == $link) {
    $output = '[ ' . $href . ' ]';
  }
  else {
    $output = $link . ' [ ' . $href . ' ]';
  }
  return $output;
}

/**
 * Convert URL to absolute URL
 *
 * Strings containing ':/' are regarded absolute and not converted.
 * Any 'mailto:' is stripped from the text.
 * 
 * @param string $url
 *   URL to be converted
 * @return string
 *   Absolute url
 */
function _simplenews_mail_url($url) {
  if (preg_match('@://@', $url)) {
    return $url;
  }
  elseif (preg_match('!mailto:!i', $url)) {
    return str_replace('mailto:', '', $url);
  }
  else {
    return url($url, NULL, NULL, 1);
  }
}

/**
 * Collect and store URLs
 *
 * @param string $url
 *   URL to be converted to absolute and stored
 * @param boolean $refresh
 *   TRUE: URL storage is cleared.
 * @return mixed
 *   if param $url is URL to be converted: Number of URLs currently stored.
 *   if param $url is empty: Keyed array of converted URLs.
 */
function _simplenews_mail_urls($url = 0, $refresh = FALSE) {
  static $urls = array();
  if ($refresh) {
    $urls = array();
  }
  if ($url) {
    $urls[] = _simplenews_mail_url($url);
    return count($urls);
  }
  return $urls;
}

/**
 *  List of preg* regular expression patterns to search for,
 *  used in conjunction with $replace.
 *  Based on / modified from html2txt.module
 */
function _simplenews_define_search() {
  $search = array(
    "/\r/",
    // Non-legal carriage return
    "/[\t]+/",
    // tabs
    '/<script[^>]*>.*?<\\/script>/i',
    // <script>s -- which strip_tags supposedly has problems with
    '!<h[12][^>]*>(.+?)</h[12]>!ie',
    // H1 - H2
    '/<h3[^>]*>(.+?)<\\/h3>/ie',
    // H3
    '/<h[456][^>]*>(.+?)<\\/h[456]>/ie',
    // H4 - H6
    '!<blockquote[^>]*>!i',
    // <blockquote>
    '!</blockquote>!i',
    // </blockquote>
    '!</?p[^>]*>!i',
    // <p>
    "/<br[^>]*>\n?/i",
    // <br>
    '!</?(b|strong)[^>]*>!i',
    // <b> and <strong>
    '!</?(em|i)[^>\\w]*>!i',
    // <em> and <i>
    '/(<ul[^>]*>|<\\/ul>)/i',
    // <ul> and </ul>
    '/(<ol[^>]*>|<\\/ol>)/i',
    // <ol> and </ol>
    '/(<dl[^>]*>|<\\/dl>)/i',
    // <dl> and </dl>
    '!<li[^>]*>!i',
    // <li>
    '!</li>!i',
    // </li>
    '!<dt[^>]*>!i',
    // <dt>
    '!</dt>!i',
    // </dt>
    '!<dd[^>]*>!i',
    // <dd>
    '!</dd>!i',
    // </dd>
    '/<hr[^>]*>/i',
    // <hr>
    '/(<table[^>]*>|<\\/table>)/i',
    // <table> and </table>
    '/(<tr[^>]*>|<\\/tr>)/i',
    // <tr> and </tr>
    '/<td[^>]*>(.+?)<\\/td>/i',
    // <td> and </td>
    '/<th[^>]*>(.+?)<\\/th>/i',
    // <th> and </th>
    '/&nbsp;/i',
    '/&quot;/i',
    '/&gt;/i',
    '/&lt;/i',
    '/&amp;/i',
    '/&copy;/i',
    '/&trade;/i',
    '/&#8220;/',
    '/&#8221;/',
    '/&#8211;/',
    '/&#8217;/',
    '/&#38;/',
    '/&#169;/',
    '/&#8482;/',
    '/&#151;/',
    '/&#147;/',
    '/&#148;/',
    '/&#149;/',
    '/&reg;/i',
    '/&bull;/i',
    '/&[&;]+;/i',
  );
  return $search;
}

/**
 *  List of pattern replacements corresponding to patterns searched.
 *  Based on / modified from html2txt.module
 */
function _simplenews_define_replace() {
  $replace = array(
    '',
    // Non-legal carriage return
    ' ',
    // Tabs
    '',
    // <script>s -- which strip_tags supposedly has problems with
    "\"\n------------\" .strtoupper('\\1'). \"------------\n\n\"",
    // H1 - H2
    "\"\n\" . strtoupper('\\1') . \"\n\n\"",
    // H3
    "ucwords(\"\n\n\\1\n\n\")",
    // H4 - H6
    "\n\n\t",
    // <blockquote>
    "\n\n",
    // </blockquote>
    "\n\n",
    // <p>
    "\n",
    // <br>
    '*',
    // <b> and <strong>
    '/',
    // <em> and <i>
    "\n\n",
    // <ul> and </ul>
    "\n\n",
    // <ol> and </ol>
    "\n\n",
    // <dl> and </dl>
    "  * ",
    // <li>
    "\n",
    // </li>
    "  * ",
    // <dt>
    "\n",
    // </dt>
    "\t",
    // <dd>
    "\n",
    // </dd>
    "\n-------------------------\n",
    // <hr>
    "\n\n",
    // <table> and </table>
    "\n",
    // <tr> and </tr>
    "\t\t\\1\n",
    // <td> and </td>
    "strtoupper(\"\t\t\\1\n\")",
    // <th> and </th>
    ' ',
    '"',
    '>',
    '<',
    '&',
    '(c)',
    '(tm)',
    '"',
    '"',
    '-',
    "'",
    '&',
    '(c)',
    '(tm)',
    '--',
    '"',
    '"',
    '*',
    '(R)',
    '*',
    '',
  );
  return $replace;
}

/**
 * Callback function to activate the specified subscriptions.
 * 
 * @param $snid array of snid's
 */
function simplenews_activate_subscription($snid) {
  db_query('UPDATE {simplenews_subscriptions} SET a_status = 1 WHERE snid IN(' . implode(',', array_fill(0, count($snid), '%d')) . ')', $snid);
}

/**
 * Callback function to inactivate the specified subscriptions.
 * 
 * @param $snid array of snid's
 */
function simplenews_inactivate_subscription($snid) {
  db_query('UPDATE {simplenews_subscriptions} SET a_status = 0 WHERE snid IN(' . implode(',', array_fill(0, count($snid), '%d')) . ')', $snid);
}

/**
 * Store filter values in session var
 * 
 * @param $type identification string
 * @param $form_values array of values to be stored
 */
function simplenews_set_filter($type, $form_values) {
  if (empty($_SESSION[$type])) {
    $_SESSION[$type] = 'all';
  }
  $op = $form_values['op'];
  if ($op == t('Filter') && isset($form_values['filter'])) {
    $_SESSION[$type] = $form_values['filter'];
  }
}

/**
 * Built filter selection box options and filter query where clause
 * 
 * @param $type identification string
 * @param $na TRUE for orphaned newsletters
 * 
 * @return array of filter selection box options and related query where clause
 */
function simplenews_get_filter($type, $na = TRUE) {

  //Default data
  $names['all'] = t('all newsletters');
  $queries['all'] = '';
  if ($na) {
    $names['na'] = t('orphaned newsletters');
    $queries['na'] = ' AND s.tid = 0';
  }

  // Data for each newsletter
  foreach (taxonomy_get_tree(variable_get('simplenews_vid', '')) as $newsletter) {
    $names[$newsletter->tid] = $newsletter->name;
    $queries[$newsletter->tid] = ' AND s.tid = ' . $newsletter->tid;
  }
  return array(
    $names,
    $queries,
  );
}
function simplenews_admin_news($action = 'sent') {
  $vid = variable_get('simplenews_vid', '');
  $form = array();
  $form['simplenews_issue_filter'] = array(
    '#type' => 'fieldset',
    '#title' => t('Show issues from'),
    '#collapsible' => FALSE,
    '#prefix' => '<div class="container-inline">',
    '#suffix' => '</div>',
  );
  $header = array(
    t('Title'),
    t('Newsletter'),
    t('Date created'),
    t('Published'),
    t('Sent'),
    t('Edit'),
  );
  list($names, $queries) = array_values(simplenews_get_filter('simplenews_newsletters_filter'));
  if ($action == 'notsent') {
    $form['simplenews_issue_filter']['filter'] = array(
      '#type' => 'select',
      '#options' => $names,
      '#default_value' => $_SESSION['simplenews_newsletters_filter'],
    );
    $query = "SELECT DISTINCT n.*, s.s_status FROM {node} n INNER JOIN {simplenews_newsletters} s ON n.nid = s.nid WHERE s.s_status = %d" . $queries[$_SESSION['simplenews_newsletters_filter']] . " ORDER BY n.created DESC";
    $count_query = "SELECT COUNT(n.nid) FROM {node} n INNER JOIN {simplenews_newsletters} s ON n.nid = s.nid WHERE s.s_status = %d" . $queries[$_SESSION['simplenews_newsletters_filter']];
  }
  else {
    $form['simplenews_issue_filter']['filter'] = array(
      '#type' => 'select',
      '#options' => $names,
      '#default_value' => $_SESSION['simplenews_newsletters_filter'],
    );
    $query = "SELECT n.*, s.s_status FROM {node} n INNER JOIN {simplenews_newsletters} s ON n.nid = s.nid WHERE s.s_status > %d" . $queries[$_SESSION['simplenews_newsletters_filter']] . " ORDER BY n.created DESC";
    $count_query = "SELECT COUNT(n.nid) FROM {node} n INNER JOIN {simplenews_newsletters} s ON n.nid = s.nid WHERE s.s_status > %d" . $queries[$_SESSION['simplenews_newsletters_filter']];
  }
  $result = pager_query(db_rewrite_sql($query), 10, 0, db_rewrite_sql($count_query), 0);
  while ($node = db_fetch_object($result)) {
    $terms = array_keys(taxonomy_node_get_terms_by_vocabulary($node->nid, $vid, 'name'));
    $rows[] = array(
      l($node->title, 'node/' . $node->nid),
      $terms[0] ? $terms[0] : t('n/a'),
      format_date($node->created, 'small'),
      theme('simplenews_status', $node->status, 'published'),
      theme('simplenews_status', $node->s_status, 'sent'),
      l(t('edit'), 'node/' . $node->nid . '/edit', array(), drupal_get_destination()),
    );
  }
  if ($pager = theme('pager', NULL, 10, 0)) {
    $rows[] = array(
      array(
        'data' => $pager,
        'colspan' => '6',
      ),
    );
  }
  if (!$rows) {
    $rows[] = array(
      array(
        'data' => t('No newsletters available.'),
        'colspan' => '6',
      ),
    );
  }
  $form['simplenews_issue_filter']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Filter'),
  );
  $table = theme('table', $header, $rows);
  $form['table'] = array(
    '#value' => $table,
  );
  return $form;
}
function simplenews_admin_news_submit($form_id, $form_values) {
  simplenews_set_filter('simplenews_newsletters_filter', $form_values);
}
function theme_simplenews_status($status, $source) {
  switch ($source) {
    case 'published':
      $imgs = array(
        0 => 'sn-saved',
        1 => 'sn-sent',
      );
      $title = array(
        0 => t('Not published'),
        1 => t('Published'),
      );
      break;
    case 'activated':
      $imgs = array(
        0 => 'sn-saved',
        1 => 'sn-sent',
      );
      $title = array(
        0 => t('Inactive: no newsletters will be sent'),
        1 => t('Active: user will receive newsletters'),
      );
      break;
    case 'sent':
      $imgs = array(
        0 => 'sn-saved',
        1 => 'sn-cron',
        2 => 'sn-sent',
      );
      $title = array(
        0 => t('Not sent'),
        1 => t('Currently sending by cron'),
        2 => t('Sent'),
      );
      break;
  }
  $path = base_path() . drupal_get_path('module', 'simplenews') . '/';
  $output = '<img src="' . $path . $imgs[$status] . '.png" width="15" height="15" alt="' . $status . '" border="0" title="' . $title[$status] . '" />';
  return $output;
}

/**
 * Generate subscription filters
 */
function simplenews_subscription_filters() {

  // Newsletter filter
  $filters['newsletter'] = array(
    'title' => t('Subscribed to'),
    'options' => array(
      'all' => t('All newsletters'),
    ),
  );
  foreach (taxonomy_get_tree(variable_get('simplenews_vid', '')) as $newsletter) {
    $filters['newsletter']['options']['tid-' . $newsletter->tid] = $newsletter->name;
  }

  // E-mail filter
  $filters['email'] = array(
    'title' => t('E-mail address'),
  );
  return $filters;
}

/**
 * Build query for subscription filters based on session var content. 
 * 
 * @return array of SQL query parts 
 *   array('where' => $where, 'join' => $join, 'args' => $args)
 */
function simplenews_build_subscription_filter_query() {

  // Variables $args and $join are currently not used but left in for future extensions
  $where = $args = array();
  $join = '';

  // Build query
  foreach ($_SESSION['simplenews_subscriptions_filter'] as $key => $value) {
    switch ($key) {
      case 'newsletter':
        list($key, $value) = explode('-', $value, 2);
        if ($key != 'all') {
          $where[] = 's.' . $key . ' = ' . $value;
        }
        break;
      case 'email':
        if (!empty($value)) {
          $where[] = 'ss.mail LIKE "%%' . db_escape_string($value) . '%%"';
        }
        break;
    }
    $args[] = $value;
  }

  // All conditions are combined with AND
  $where = empty($where) ? '' : ' AND ' . implode(' AND ', $where);
  return array(
    'where' => $where,
    'join' => $join,
    'args' => $args,
  );
}

/**
 * Return form for subscription filters.
 */
function simplenews_subscription_filter_form() {

  // Current filter selections in $session var; stored at form submission
  // Example: array('newsletter' => 'all', 'email' => 'hotmail')
  $session =& $_SESSION['simplenews_subscriptions_filter'];
  $session = is_array($session) ? $session : _simplenews_subscription_filter_default();
  $filters = simplenews_subscription_filters();
  $form['filters'] = array(
    '#type' => 'fieldset',
    '#title' => t('Subscription filters'),
    '#collapsible' => FALSE,
    '#prefix' => '<div class="simplenews-subscription-filter">',
    '#suffix' => '</div>',
  );

  // Filter values are default
  $form['filters']['newsletter'] = array(
    '#type' => 'select',
    '#title' => $filters['newsletter']['title'],
    '#options' => $filters['newsletter']['options'],
    '#default_value' => $session['newsletter'],
  );
  $form['filters']['email'] = array(
    '#type' => 'textfield',
    '#title' => $filters['email']['title'],
    '#default_value' => $session['email'],
  );
  $form['filters']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Filter'),
    '#prefix' => '<span class="spacer" />',
  );

  // Add Reset button if filter is in use
  if ($session != _simplenews_subscription_filter_default()) {
    $form['filters']['reset'] = array(
      '#type' => 'submit',
      '#value' => t('Reset'),
    );
  }
  return $form;
}

/**
 * Helper function: returns subscription filter default settings
 */
function _simplenews_subscription_filter_default() {
  return array(
    'newsletter' => 'all',
    'email' => '',
  );
}
function simplenews_subscription_filter_form_submit($form_id, $form_values) {
  switch ($form_values['op']) {
    case t('Filter'):
      $_SESSION['simplenews_subscriptions_filter'] = array(
        'newsletter' => $form_values['newsletter'],
        'email' => $form_values['email'],
      );
      break;
    case t('Reset'):
      $_SESSION['simplenews_subscriptions_filter'] = _simplenews_subscription_filter_default();
      break;
  }
}

/**
 * Menu callback: subscription administration.
 */
function simplenews_subscription_admin() {

  // Deleting subscriptions requires delete confirmation. This is handled with a different form
  if ($_POST['operation'] == 'delete' && $_POST['snids']) {
    if ($_REQUEST['destination']) {
      $destination = drupal_get_destination();
      unset($_REQUEST['destination']);
    }
    $_SESSION['simplenews_subscriptions_delete'] = $_POST['snids'];

    // Note: we redirect from admin/content/newsletters/users to admin/content/newsletters/subscriptions/delete to make the tabs disappear.
    drupal_goto("admin/content/newsletters/subscriptions/delete", $destination);
  }

  // Call the filter form first, to allow for the form_values array to be populated.
  $output = drupal_get_form('simplenews_subscription_filter_form');
  $output .= drupal_get_form('simplenews_subscription_list_form');
  return $output;
}
function simplenews_subscription_list_form() {
  global $form_values;

  // Table header. Used as tablesort default
  $header = array(
    array(
      'data' => t('E-mail'),
      'field' => 'ss.mail',
      'sort' => 'asc',
    ),
    array(
      'data' => t('Username'),
      'field' => 'u.name',
    ),
    array(
      'data' => t('Status'),
      'field' => 'ss.a_status',
    ),
    t('Operations'),
  );

  // Data collection with filter and sorting applied
  $filter = simplenews_build_subscription_filter_query();
  $query = 'SELECT DISTINCT ss.*, u.name FROM {simplenews_subscriptions} ss INNER JOIN {users} u ON ss.uid = u.uid INNER JOIN {simplenews_snid_tid} s ON ss.snid = s.snid' . $filter['where'];
  $count_query = preg_replace('/SELECT.*FROM /', 'SELECT COUNT(DISTINCT ss.mail) FROM ', $query);
  $query .= tablesort_sql($header);

  // Use count_query to count distinct records only
  $result = pager_query($query, 30, 0, $count_query);

  // Update options
  $form['options'] = array(
    '#type' => 'fieldset',
    '#title' => t('Update options'),
    '#prefix' => '<div class="container-inline">',
    '#suffix' => '</div>',
  );
  $form['options']['operation'] = array(
    '#type' => 'select',
    '#options' => array(
      'activate' => t('Activate'),
      'inactivate' => t('Inactivate'),
      'delete' => t('Delete'),
    ),
    '#default_value' => 'activate',
  );
  $form['options']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Update'),
  );

  // Subscription table and table pager
  while ($subs = db_fetch_object($result)) {
    $snids[$subs->snid] = '';
    $form['mail'][$subs->snid] = array(
      '#value' => $subs->mail,
    );
    $form['name'][$subs->snid] = array(
      '#value' => $subs->uid ? l($subs->name, 'user/' . $subs->uid) : $subs->name,
    );
    $form['status'][$subs->snid] = array(
      '#value' => theme('simplenews_status', $subs->a_status, 'activated'),
    );
    $form['operations'][$subs->snid] = array(
      '#value' => l(t('edit'), 'admin/content/newsletters/users/edit/' . $subs->snid, array(), drupal_get_destination()),
    );
  }
  $form['snids'] = array(
    '#type' => 'checkboxes',
    '#options' => $snids,
  );
  $form['pager'] = array(
    '#value' => theme('pager', NULL, 30, 0),
  );
  return $form;
}
function simplenews_subscription_list_form_validate($form_id, $form_values) {
  if (isset($form_values['operation'])) {
    $snids = array_filter($form_values['snids']);
    if (empty($snids)) {
      form_set_error('', t('No items selected.'));
    }
  }
}

/**
 * Submit the subscription administration update form.
 */
function simplenews_subscription_list_form_submit($form_id, $form_values) {
  if (isset($form_values['operation'])) {
    $snids = array_filter($form_values['snids']);
    $args = array(
      $snids,
    );
    switch ($form_values['operation']) {
      case 'activate':
        call_user_func_array('simplenews_activate_subscription', $args);
        drupal_set_message(t('The update has been performed.'));
        break;
      case 'inactivate':
        call_user_func_array('simplenews_inactivate_subscription', $args);
        drupal_set_message(t('The update has been performed.'));
        break;
    }
  }
}

/**
 * Theme subscription administration overview.
 */
function theme_simplenews_subscription_list_form($form) {

  // Subscription table header
  $header = array(
    theme('table_select_header_cell'),
    array(
      'data' => t('E-mail'),
      'field' => 'ss.mail',
      'sort' => 'asc',
    ),
    array(
      'data' => t('Username'),
      'field' => 'u.name',
    ),
    array(
      'data' => t('Status'),
      'field' => 'ss.a_status',
    ),
    t('Operations'),
  );

  // Subscription table
  $output = drupal_render($form['options']);
  if (isset($form['mail']) && is_array($form['mail'])) {
    foreach (element_children($form['mail']) as $key) {
      $row = array();
      $row[] = drupal_render($form['snids'][$key]);
      $row[] = drupal_render($form['mail'][$key]);
      $row[] = drupal_render($form['name'][$key]);
      $row[] = drupal_render($form['status'][$key]);
      $row[] = drupal_render($form['operations'][$key]);
      $rows[] = $row;
    }
  }
  else {
    $rows[] = array(
      array(
        'data' => t('No subscriptions available.'),
        'colspan' => '4',
      ),
    );
  }

  // Render table header, pager and form
  $output .= theme('table', $header, $rows);
  if ($form['pager']['#value']) {
    $output .= drupal_render($form['pager']);
  }
  $output .= drupal_render($form);
  return $output;
}

/**
 * Delete multiple subscriptions
 */
function simplenews_subscription_multiple_delete_confirm() {

  // Subscriptions to be deleted are passed via session var.
  $snids = $_SESSION['simplenews_subscriptions_delete'];
  $form['notice'] = array(
    '#value' => t('These user(s) will be unsubscribed from any and all Newsletters to which they are currently subscribed. This will not delete related user account(s) from this site.'),
  );
  $form['snids'] = array(
    '#prefix' => '<ul>',
    '#suffix' => '</ul>',
    '#tree' => TRUE,
  );

  // array_filter returns only elements with TRUE values
  foreach (array_filter($snids) as $snid => $value) {
    $mail = db_result(db_query('SELECT mail FROM {simplenews_subscriptions} WHERE snid = %d', $snid));
    $form['snids'][$snid] = array(
      '#type' => 'hidden',
      '#value' => $snid,
      '#prefix' => '<li>',
      '#suffix' => check_plain($mail) . "</li>\n",
    );
  }
  $form['operation'] = array(
    '#type' => 'hidden',
    '#value' => 'delete',
  );
  return confirm_form($form, t('Are you sure you want to delete these subscriptions?'), 'admin/content/newsletters/users', NULL, t('Delete all'), t('Cancel'));
}
function simplenews_subscription_multiple_delete_confirm_submit($form_id, $form_values) {
  if ($form_values['confirm']) {
    foreach ($form_values['snids'] as $snid => $value) {
      simplenews_delete_subscription($snid);
    }
    drupal_set_message(t('The subscriptions have been deleted.'));
  }

  // Route the user back to the subscription admin form
  return 'admin/content/newsletters/users';
}
function simplenews_subscription_list_add() {
  $form['emails'] = array(
    '#type' => 'textarea',
    '#title' => t('E-mail addresses'),
    '#cols' => 60,
    '#rows' => 5,
    '#description' => t('Supply a comma separated list of e-mail addresses to be added to the list. Spaces between commas and addresses are allowed.'),
  );
  $newsletters = array();
  foreach (taxonomy_get_tree(variable_get('simplenews_vid', '')) as $newsletter) {
    $newsletters[$newsletter->tid] = check_plain($newsletter->name);
  }
  $form['newsletters'] = array(
    '#type' => 'fieldset',
    '#title' => t('Subscribe imported addresses to the following newsletter(s)'),
    '#collapsible' => FALSE,
  );
  $form['newsletters']['newsletters'] = array(
    '#type' => 'checkboxes',
    '#options' => $newsletters,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Import'),
  );
  return $form;
}
function simplenews_subscription_list_add_validate($form_id, $form_values) {
  $checked_newsletters = array_filter($form_values['newsletters']);
  if (!$checked_newsletters) {
    form_set_error('newsletters', t('You must select at least one newsletter.'));
  }
}
function simplenews_subscription_list_add_submit($form_id, $form_values) {
  $tree = taxonomy_get_tree(variable_get('simplenews_vid', ''));
  $added = array();
  $invalid = array();
  $checked_newsletters = array_filter($form_values['newsletters']);
  $emails = explode(",", $form_values['emails']);
  foreach ($emails as $email) {
    $email = trim($email);
    if (valid_email_address($email)) {
      foreach ($checked_newsletters as $tid) {
        $newsletter = taxonomy_get_term($tid);
        simplenews_subscribe_user($email, $newsletter->tid, FALSE);
        $added[] = $email;
      }
    }
    else {
      $invalid[] = $email;
    }
  }
  if ($added) {
    $added = implode(", ", $added);
    drupal_set_message(t('The following addresses were added or updated: %added.', array(
      '%added' => $added,
    )));
    $newsletter_names = array();
    foreach ($checked_newsletters as $tid) {
      $newsletter = taxonomy_get_term($tid);
      $newsletter_names[] = $newsletter->name;
    }
    drupal_set_message(t('The addresses were subscribed to the following newsletters: %newsletters.', array(
      '%newsletters' => implode(', ', $newsletter_names),
    )));
  }
  else {
    drupal_set_message(t('No addresses were added.'));
  }
  if ($invalid) {
    $invalid = implode(", ", $invalid);
    drupal_set_message(t('The following addresses were invalid: %invalid.', array(
      '%invalid' => $invalid,
    )), 'error');
  }
}
function simplenews_subscription_list_export() {
  $tree = taxonomy_get_tree(variable_get('simplenews_vid', ''));
  $form['simplenews_status'] = array(
    '#type' => 'fieldset',
    '#title' => t('Status'),
    '#description' => t('Select at least 1 status'),
    '#collapsible' => FALSE,
  );
  $form['simplenews_status']['active'] = array(
    '#type' => 'checkbox',
    '#title' => t('Active users'),
    '#return_value' => 1,
  );
  $form['simplenews_status']['inactive'] = array(
    '#type' => 'checkbox',
    '#title' => t('Inactive users'),
    '#return_value' => 1,
  );
  if ($tree) {
    $form['simplenews_newsletter'] = array(
      '#type' => 'fieldset',
      '#title' => t('Subscribed to'),
      '#description' => t('Select at least 1 newsletter'),
      '#collapsible' => FALSE,
    );
    foreach ($tree as $newsletter) {
      $form['simplenews_newsletter']['tid_' . $newsletter->tid] = array(
        '#type' => 'checkbox',
        '#title' => check_plain($newsletter->name),
        '#return_value' => 1,
      );
    }
  }
  $form['emails'] = array(
    '#type' => 'textarea',
    '#title' => t('E-mail addresses'),
    '#cols' => 60,
    '#rows' => 5,
    '#default_value' => t('No search performed'),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Export'),
  );
  $form['#after_build'] = array(
    'simplenews_admin_export_after_build',
  );
  $form['#redirect'] = FALSE;
  return $form;
}
function simplenews_admin_export_after_build($form, $edit) {
  $op = isset($_POST['op']) ? $_POST['op'] : '';
  if ($op == t('Export')) {
    $tree = taxonomy_get_tree(variable_get('simplenews_vid', ''));
    if ($edit['active'] == 1) {
      $where[] = 's.a_status = 1';
    }
    if ($edit['inactive'] == 1) {
      $where[] = 's.a_status = 0';
    }
    $where = $where ? implode(' OR ', $where) : NULL;
    if ($tree) {
      foreach ($tree as $newsletter) {
        if ($edit['tid_' . $newsletter->tid]) {
          $where_tid[] = 't.tid = ' . $newsletter->tid;
        }
      }
    }
    $where_tid = $where_tid ? implode(' OR ', $where_tid) : NULL;
    if ($where && $where_tid) {
      $query = 'SELECT DISTINCT s.mail FROM {simplenews_subscriptions} s INNER JOIN {simplenews_snid_tid} t ON s.snid = t.snid WHERE (' . $where . ') AND (' . $where_tid . ')';
      $result = db_query($query);
      while ($mail = db_fetch_object($result)) {
        $mails[] = $mail->mail;
      }
    }
    if ($mails) {
      $exported_mails = implode(", ", $mails);
    }
    else {
      $exported_mails = t('No addresses were found.');
    }
    $form['emails']['#value'] = $exported_mails;
  }
  return $form;
}

/**
 * Generate settings form.
 */
function simplenews_admin_settings($tid = NULL) {
  $address_default = variable_get('site_mail', ini_get('sendmail_from'));
  $name_default = variable_get('site_name', 'drupal');
  $form = array();
  $form['#validate']['simplenews_admin_settings_validate'] = array();
  if (isset($tid) && ($term = taxonomy_get_term($tid))) {

    // Add the term ID as an argument to the settings validation function.
    $form['#validate']['simplenews_admin_settings_validate'][] = $term->tid;
    $form['simplenews_sender_information'] = array(
      '#type' => 'fieldset',
      '#title' => t('Sender information'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $form['simplenews_sender_information']['simplenews_from_name_' . $term->tid] = array(
      '#type' => 'textfield',
      '#title' => t('From name'),
      '#size' => 60,
      '#maxlength' => 128,
      '#default_value' => variable_get('simplenews_from_name_' . $term->tid, variable_get('simplenews_from_name', $name_default)),
    );
    $form['simplenews_sender_information']['simplenews_from_address_' . $term->tid] = array(
      '#type' => 'textfield',
      '#title' => t('From e-mail address'),
      '#size' => 60,
      '#maxlength' => 128,
      '#required' => TRUE,
      '#default_value' => variable_get('simplenews_from_address_' . $term->tid, variable_get('simplenews_from_address', $address_default)),
    );
    $form['simplenews_hyperlinks'] = array(
      '#type' => 'fieldset',
      '#title' => t('HTML to text conversion'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#description' => t('When your newsletter is sent as plain text, these options will determine how the conversion to text is performed.'),
    );
    $form['simplenews_hyperlinks']['simplenews_hyperlinks_' . $term->tid] = array(
      '#type' => 'radios',
      '#title' => t('Hyperlink conversion'),
      '#options' => array(
        t('Append hyperlinks and links to images as a numbered reference list'),
        t('Display hyperlinks and links to image inline with the text'),
      ),
      '#default_value' => variable_get('simplenews_hyperlinks_' . $term->tid, 1),
    );
  }
  else {
    $form['simplenews_default_options'] = array(
      '#type' => 'fieldset',
      '#title' => t('Default newsletter options'),
      '#collapsible' => TRUE,
      '#collapsed' => FALSE,
      '#description' => t('These options will be the defaults for new newsletters, but can be overridden in the newsletter editing form.'),
    );
    $form['simplenews_default_options']['simplenews_format'] = array(
      '#type' => 'select',
      '#title' => t('Format'),
      '#options' => _simplenews_format_options(),
      '#description' => t('Select the default newsletter sending format.'),
      '#default_value' => variable_get('simplenews_format', 'plain'),
    );
    $form['simplenews_default_options']['simplenews_priority'] = array(
      '#type' => 'select',
      '#title' => t('Priority'),
      '#options' => array(
        0 => t('none'),
        1 => t('highest'),
        2 => t('high'),
        3 => t('normal'),
        4 => t('low'),
        5 => t('lowest'),
      ),
      '#description' => t('Note that e-mail priority is ignored by a lot of e-mail programs.'),
      '#default_value' => variable_get('simplenews_priority', 0),
    );
    $form['simplenews_default_options']['simplenews_receipt'] = array(
      '#type' => 'checkbox',
      '#title' => t('Request receipt'),
      '#return_value' => 1,
      '#default_value' => variable_get('simplenews_receipt', 0),
      '#description' => t('Request a Read Receipt from your newsletters. A lot of e-mail programs ignore these so it is not a definitive indication of how many people have read your newsletter.'),
    );
    $form['simplenews_default_options']['simplenews_send'] = array(
      '#type' => 'radios',
      '#title' => t('Default selection for sending newsletters'),
      '#options' => array(
        0 => t("Don't send now"),
        2 => t('Send one test newsletter to the test address'),
        1 => t('Send newsletter'),
      ),
      '#default_value' => variable_get('simplenews_send', 0),
    );
    $form['simplenews_test_address'] = array(
      '#type' => 'fieldset',
      '#title' => t('Test addresses options'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#description' => t('Supply a comma-separated list of e-mail addresses to be used as test addresses. The override function allows to override these addresses upon newsletter creation.'),
    );
    $form['simplenews_test_address']['simplenews_test_address'] = array(
      '#type' => 'textfield',
      '#title' => t('Test e-mail address'),
      '#size' => 60,
      '#maxlength' => 128,
      '#default_value' => variable_get('simplenews_test_address', $address_default),
    );
    $form['simplenews_test_address']['simplenews_test_address_override'] = array(
      '#type' => 'checkbox',
      '#title' => t('Allow test address override'),
      '#return_value' => 1,
      '#default_value' => variable_get('simplenews_test_address_override', 0),
    );
    $form['simplenews_sender_info'] = array(
      '#type' => 'fieldset',
      '#title' => t('Sender information'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#description' => t('Default sender address that will only be used for confirmation e-mails. You can specify sender information for each newsletter separately on the newsletter\'s settings page.'),
    );
    $form['simplenews_sender_info']['simplenews_from_name'] = array(
      '#type' => 'textfield',
      '#title' => t('From name'),
      '#size' => 60,
      '#maxlength' => 128,
      '#default_value' => variable_get('simplenews_from_name', $name_default),
    );
    $form['simplenews_sender_info']['simplenews_from_address'] = array(
      '#type' => 'textfield',
      '#title' => t('From e-mail address'),
      '#size' => 60,
      '#maxlength' => 128,
      '#required' => TRUE,
      '#default_value' => variable_get('simplenews_from_address', $address_default),
    );
    $form['simplenews_subscription'] = array(
      '#type' => 'fieldset',
      '#title' => t('Subscription options'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $form['simplenews_subscription']['simplenews_sync_account'] = array(
      '#type' => 'checkbox',
      '#title' => t('Synchronize with account'),
      '#default_value' => variable_get('simplenews_sync_account', FALSE),
      '#description' => t('When checked subscriptions will be synchronized with site accounts. When accounts are deteted, subscriptions with the same email address will be removed. When site accounts are blocked/unblocked, subscriptions will be deactivated/activated. When not checked subscriptions will be unchanged when associated accounts are deleted or blocked.'),
    );
    $form['simplenews_mail_backend'] = array(
      '#type' => 'fieldset',
      '#title' => t('Mail backend'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $max_time = array(
      0,
      1,
      2,
      3,
      4,
    );
    for ($i = 5; $i < ini_get('max_execution_time'); $i = $i + 5) {
      $max_time[] = $i;
    }
    $form['simplenews_mail_backend']['simplenews_time'] = array(
      '#type' => 'select',
      '#title' => t('Initial send time'),
      '#options' => drupal_map_assoc($max_time),
      '#default_value' => variable_get('simplenews_time', 2),
      '#description' => t('Sets the maximum time in seconds during which newsletters are sent. If not all recipients have been mailed within this time, sending is further handled as a cronjob.') . ' <br /> ' . t('The highest value in the dropdown list is based on max_execution_time in your php.ini file. Note that if not all previous newsletters have been sent to all recipients yet, these are sent first.'),
    );
    $throttle = drupal_map_assoc(array(
      1,
      10,
      20,
      30,
      40,
      50,
      100,
      200,
      300,
      400,
      500,
      1000,
      2000,
      3000,
      4000,
      5000,
    ));
    $throttle[999999] = t('Unlimited');
    $form['simplenews_mail_backend']['simplenews_throttle'] = array(
      '#type' => 'select',
      '#title' => t('Cron throttle'),
      '#options' => $throttle,
      '#default_value' => variable_get('simplenews_throttle', 20),
      '#description' => t('Sets the numbers of newsletters sent per cron run. Failure to send will also be counted.'),
    );
    $form['simplenews_mail_backend']['simplenews_debug'] = array(
      '#type' => 'checkbox',
      '#title' => t('Log emails'),
      '#default_value' => variable_get('simplenews_debug', FALSE),
      '#description' => t('When checked all outgoing simplenews emails are logged in the system log. A logged email does not guarantee that it is send or will be delivered. It only indicates that a message is send to the PHP mail() function. No status information is available of delivery by the PHP mail() function.'),
    );
  }
  return system_settings_form($form);
}

/**
 * Forms API callback; validates the settings form.
 */
function simplenews_admin_settings_validate($form_id, $form_values, $form, $tid = NULL) {
  $field_name = 'simplenews_from_address';

  // If tid is set, we're validating specific newsletter settings so update field name.
  if (isset($tid)) {
    $field_name .= '_' . $tid;
  }
  if (!valid_email_address($form_values[$field_name])) {
    form_set_error($field_name, t("The sender's e-mail address you supplied is not valid."));
  }
}
function simplenews_types_overview() {
  $rows = array();
  $header = array(
    t('Newsletter name'),
    t('Operations'),
  );
  foreach (taxonomy_get_tree(variable_get('simplenews_vid', '')) as $term) {
    $rows[] = array(
      check_plain($term->name),
      l(t('edit newsletter'), 'admin/content/newsletters/types/edit/' . $term->tid),
    );
  }
  if (count($rows) == 0) {
    $rows[] = array(
      array(
        'data' => t('There are currently no newsletter series.'),
        'colspan' => 2,
      ),
    );
  }
  return theme('table', $header, $rows);
}

/**
 * Menu callback; handles the edit/delete subscription page and a subscription
 * page for anonymous users.
 */
function simplenews_admin_users_form($snid = NULL) {
  if (isset($snid) && $_POST['op'] == t('Delete')) {

    // Kill destination redirect.
    unset($_REQUEST['destination']);
    drupal_goto('admin/content/newsletters/users/delete/' . $snid);
  }
  return simplenews_subscription_manager_form($snid);
}

/**
 * Forms API callback; delete user subscription form. Form to delete user from
 * all newsletters.
 */
function simplenews_admin_users_delete($snid = NULL) {
  if (!isset($snid)) {
    drupal_not_found();
    return;
  }
  $subscription = simplenews_get_subscription($snid);
  $form = array();
  $form['snid'] = array(
    '#type' => 'value',
    '#value' => $snid,
  );
  return confirm_form($form, t('Are you sure you want to remove %mail from all newsletter subscription lists?', array(
    '%mail' => $subscription->mail,
  )), 'admin/content/newsletters/users', t('This action will remove %mail from all newsletter subscription lists. To unsubscribe this user from a particular newsletter, press Cancel and edit this user.', array(
    '%mail' => $subscription->mail,
  )), t('Delete'), t('Cancel'));
}

/**
 * Forms API callback; delete user subscription submit.
 */
function simplenews_admin_users_delete_submit($form_id, $form_values) {
  if ($form_values['confirm']) {
    $account = db_fetch_object(db_query('SELECT mail FROM {simplenews_subscriptions} WHERE snid = %d', $form_values['snid']));
    simplenews_delete_subscription($form_values['snid']);
    drupal_set_message(t('The user %user was successfully deleted from the subscription list.', array(
      '%user' => $account->mail,
    )));
    return 'admin/content/newsletters/users';
  }
}

/**
 * API function; deletes every subscription for the given subscription ID.
 */
function simplenews_delete_subscription($snid) {
  $account = db_fetch_object(db_query('SELECT mail FROM {simplenews_subscriptions} WHERE snid = %d', $snid));
  db_query('DELETE FROM {simplenews_subscriptions} WHERE snid = %d', $snid);
  db_query('DELETE FROM {simplenews_snid_tid} WHERE snid = %d', $snid);
  watchdog('newsletter', t('User %email deleted from the subscription list.', array(
    '%email' => $account->mail,
  )));
}

/**
 * Forms API callback; displays newsletter (term) add/edit form.
 */
function simplenews_admin_types_form($tid = NULL) {
  if (isset($tid)) {
    $edit = (array) taxonomy_get_term($tid);

    // If the given term is not a newsletter, don't allow editing.
    if (variable_get('simplenews_vid', '') != $edit['vid']) {
      drupal_not_found();
      return;
    }

    // Redirect on a delete operation for posterity's sake.
    if ($_POST['op'] == t('Delete')) {
      drupal_goto('admin/content/newsletters/types/delete/' . $edit['tid']);
    }
  }
  else {

    // Add form so choose simplenews vocabulary.
    $edit = array(
      'vid' => variable_get('simplenews_vid', ''),
    );
  }
  $form['info'] = array(
    '#value' => t('You can create different newsletters (or subjects) to categorize your news (e.g. Cats news, Dogs news, ...).'),
  );
  $form['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Newsletter name'),
    '#size' => 50,
    '#maxlength' => 128,
    '#description' => t('This name is used to identify the newsletter.'),
    '#required' => TRUE,
    '#default_value' => isset($edit['name']) ? $edit['name'] : '',
  );
  $form['description'] = array(
    '#type' => 'textarea',
    '#title' => t('Description'),
    '#cols' => 60,
    '#rows' => 5,
    '#description' => t('The description can be used to provide more information.'),
    '#default_value' => isset($edit['description']) ? $edit['description'] : '',
  );
  $form['weight'] = array(
    '#type' => 'weight',
    '#title' => t('Weight'),
    '#delta' => 10,
    '#description' => t('In listings, the heavier (with a higher weight value) terms will sink and the lighter terms will be positioned nearer the top.'),
    '#default_value' => isset($edit['weight']) ? $edit['weight'] : 0,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
    '#weight' => 25,
  );
  $form['vid'] = array(
    '#type' => 'hidden',
    '#value' => isset($edit['vid']) ? $edit['vid'] : '',
  );

  // If we are editing a newsletter term, show delete option. When the submit
  // passes the $form_values['tid'] to taxonomy_save_term() it will delete the
  // term for some reason.
  if (isset($edit['tid'])) {
    $form['delete'] = array(
      '#type' => 'submit',
      '#value' => t('Delete'),
      '#weight' => 30,
    );
    $form['tid'] = array(
      '#type' => 'value',
      '#value' => $edit['tid'],
    );
  }
  return $form;
}

/**
 * Forms API callback; handles newsletter (term) form submit.
 */
function simplenews_admin_types_form_submit($form_id, $form_values) {
  switch (taxonomy_save_term($form_values)) {
    case SAVED_NEW:
      drupal_set_message(t('Created new term %name.', array(
        '%name' => $form_values['name'],
      )));
      break;
    case SAVED_UPDATED:
      drupal_set_message(t('Updated term %name.', array(
        '%name' => $form_values['name'],
      )));
      break;
  }
  return 'admin/content/newsletters/types';
}

/**
 * Forms API callback; newsletter (term) delete form.
 */
function simplenews_admin_types_delete($tid = NULL) {
  if (!isset($tid)) {
    drupal_not_found();
    return;
  }
  $term = taxonomy_get_term($tid);
  $form = array();
  $form['tid'] = array(
    '#type' => 'value',
    '#value' => $tid,
  );
  return confirm_form($form, t('Are you sure you want to delete %title? All subscriptions associated with this newsletter will be lost.', array(
    '%title' => $term->name,
  )), $_GET['destination'] ? $_GET['destination'] : 'admin/content/newsletters/types', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
}

/**
 * Forms API callback; handles submit for newsletter (term) delete form.
 */
function simplenews_admin_types_delete_submit($form_id, $form_values) {
  if ($form_values['confirm']) {
    $term = taxonomy_get_term($form_values['tid']);
    taxonomy_del_term($form_values['tid']);
    drupal_set_message(t('Newsletter %title has been deleted.', array(
      '%title' => $term->name,
    )));
    return 'admin/content/newsletters/types';
  }
}

/**
 * Menu callback; confirm the user's (un)subscription request when they click
 * on the confirm link in the e-mail footer.
 */
function simplenews_confirm_subscription($op1 = NULL, $op2 = NULL) {
  $md5 = drupal_substr($op2, 0, 10);
  list($snid, $tid) = explode('t', drupal_substr($op2, 10));
  $result = db_query('SELECT snid, mail FROM {simplenews_subscriptions} WHERE snid = %d', $snid);
  if (!($subs = db_fetch_object($result))) {
    drupal_not_found();
    return;
  }
  if ($md5 == drupal_substr(md5($subs->mail . simplenews_private_key()), 0, 10)) {
    $newsletter = taxonomy_get_term($tid);
    if ($op1 == 'remove') {
      return drupal_get_form('simplenews_confirm_removal_form', $subs->mail, $newsletter);
    }
    elseif ($op1 == 'add') {
      return drupal_get_form('simplenews_confirm_add_form', $subs->mail, $newsletter);
    }
  }

  // If md5 didn't match, do a not found.
  drupal_not_found();
  return;
}

/**
 * Generate the confirm add form.
 */
function simplenews_confirm_add_form($mail, $newsletter) {
  $form = array();
  $form['question'] = array(
    '#value' => '<p>' . t('Are you sure you want to add %user to the %newsletter subscription list?', array(
      '%user' => $mail,
      '%newsletter' => $newsletter->name,
    )) . "<p>\n",
  );
  $form['mail'] = array(
    '#type' => 'value',
    '#value' => $mail,
  );
  $form['newsletter'] = array(
    '#type' => 'value',
    '#value' => $newsletter,
  );
  return confirm_form($form, t('Confirm subscription'), '', t('You always have the option of unsubscribing later.'), t('Subscribe'), t('Cancel'));
}

/**
 * Forms API callback; handles form submission for a user confirming subscribe
 * request.
 */
function simplenews_confirm_add_form_submit($form_id, $form_values) {
  simplenews_subscribe_user($form_values['mail'], $form_values['newsletter']->tid, FALSE);
  drupal_set_message(t('%user was successfully added to the %newsletter subscription list.', array(
    '%user' => $form_values['mail'],
    '%newsletter' => $form_values['newsletter']->name,
  )));
  return '';
}

/**
 * Generate the confirm remove form.
 */
function simplenews_confirm_removal_form($mail, $newsletter) {
  $form = array();
  $form['question'] = array(
    '#value' => '<p>' . t('Are you sure you want to remove %user from the %newsletter subscription list?', array(
      '%user' => $mail,
      '%newsletter' => $newsletter->name,
    )) . "<p>\n",
  );
  $form['mail'] = array(
    '#type' => 'value',
    '#value' => $mail,
  );
  $form['newsletter'] = array(
    '#type' => 'value',
    '#value' => $newsletter,
  );
  return confirm_form($form, t('Confirm unsubscription'), '', t('This action will only remove you from the newsletter subscription list. If you are registered at our site, your account information will remain unchanged.'), t('Unsubscribe'), t('Cancel'));
}

/**
 * Forms API callback; handles form submission for a user confirming unsubscribe
 * request.
 */
function simplenews_confirm_removal_form_submit($form_id, $form_values) {
  simplenews_unsubscribe_user($form_values['mail'], $form_values['newsletter']->tid, FALSE);
  drupal_set_message(t('%user was successfully removed from the %newsletter subscription list.', array(
    '%user' => $form_values['mail'],
    '%newsletter' => $form_values['newsletter']->name,
  )));
  return '';
}
function simplenews_replace_vars($node, $teaser = TRUE) {
  global $base_url;
  $user_mail = db_result(db_query('SELECT mail FROM {users} WHERE uid = %d', $node->uid));
  $variables = array(
    '%site' => variable_get('site_name', 'drupal'),
    '%uri' => $base_url,
    '%uri_brief' => drupal_substr($base_url, drupal_strlen('http://')),
    '%mymail' => $user_mail,
    '%date' => format_date(time()),
    '%login_uri' => url('user/login', NULL, NULL, TRUE),
  );
  $node->body = strtr($node->body, $variables);
  if ($teaser) {
    $node->teaser = strtr($node->teaser, $variables);
  }
  return $node;
}
function simplenews_time() {
  list($usec, $sec) = explode(" ", microtime());
  return (double) $usec + (double) $sec;
}
function simplenews_private_key() {
  $key = variable_get('simplenews_private_key', FALSE);
  if (!$key) {

    //  This will create a 32 character identifier (a 128 bit hex number) that is extremely difficult to predict
    $key = md5(uniqid(rand()));
    variable_set('simplenews_private_key', $key);
  }
  return $key;
}

/**
 * Implementation of hook_help
 */
function simplenews_help($section) {
  switch ($section) {
    case 'admin/help#simplenews':
      $help = "<p>" . t('Simplenews sends html or plain text newsletters to the subscription list. Subscription and unsubscription are managed through a block, a form or by the newsletter administrator.') . "<br />\n";
      $help .= t('Individual newsletters are grouped by a newsletter taxonomy term. Newsletters can have a block with the ability of (un)subscription, listing of recent newsletters and an associated rss-feed.') . "<br />\n";
      $help .= t('Sending of large mailings can be managed by cron.') . "</p>\n";
      $help .= "<p>" . t('For more information please read the configuration and customization handbook <a href="http://drupal.org/node/197057">Simplenews page</a>.') . "</p>\n";
      return $help;
    case 'node/add/simplenews':
      $help = "<p>" . t('Add this newsletter issue to a newsletter by selecting a newsletter from the select list.') . "</p>\n";
      $help .= "<p>" . t('Send a newsletter or a test newsletter by selecting the appropriate radio button and submitting the node.') . "</p>\n";
      if (!module_exists('mimemail')) {
        $help .= "<p>" . t('Install Mime Mail module to send HTML emails. Mime Mail is also used to send emails with attachments, both plain text and HTML emails.') . "</p>\n";
      }
      return $help;
    case 'admin/content/newsletters/users':
      $help = "<p>" . t('Operations and update options:
<ul>
<li>To temporarily stop all newsletters to an e-mail address, <em>inactivate</em> this subscription. </li>
<li>To unsubscribe an e-mail address from all newsletters, <em>delete</em> this subscription.</li>
<li>To unsubscribe an e-mail address from a specific newsletter, use the <em>edit link</em> of this subscription. </li>
</ul>') . "</p>\n";
      return $help;
  }
}

/**
 * Helper function to generate the hash key used for subscribe/unsubscribe link
 * in e-mail footer.
 */
function _simplenews_generate_hash($mail, $snid, $tid) {
  return drupal_substr(md5($mail . simplenews_private_key()), 0, 10) . $snid . 't' . $tid;
}

/**
 * Helper function to determine possible mail format options. The mimemodule
 * module must be installed to send HTML mails.
 */
function _simplenews_format_options() {
  $options = array(
    'plain' => t('plain'),
  );
  if (module_exists('mimemail')) {
    $options['html'] = t('html');
  }
  return $options;
}

/**
 * Theme the newsletter message subject and body.
 */
function theme_simplenews_newsletter($node, $tid) {
  $term = taxonomy_get_term($tid);
  $node->subject = '[' . $term->name . '] ' . $node->title;
  $node->body = '<h2>' . $node->title . "</h2>\n" . $node->body;
  return $node;
}

/**
 * Theme the footer.
 */
function theme_simplenews_newsletter_footer($node, $hash) {
  if ($node->s_format == 'html') {
    $node->body .= '<p>-- <br />' . l(t('Click here to unsubscribe from this newsletter'), 'newsletter/confirm/remove/' . $hash, array(), NULL, NULL, TRUE) . '</p>';
  }
  else {
    $node->body .= "\n\n-- \n" . t('Unsubscribe from this newsletter: @url', array(
      '@url' => url('newsletter/confirm/remove/' . $hash, NULL, NULL, TRUE),
    ));
  }
  return $node;
}

/**
 * Construct the themable newsletter confirmation email.
 */
function theme_simplenews_newsletter_confirmation($email, $newsletter, $snid, $op, $hash) {
  $mail = new stdClass();
  simplenews_set_from($mail, $newsletter->tid);
  $mail->s_format = 'plain';
  $mail->priority = 'none';
  $mail->to = $email;
  $mail->subject = t('Confirmation for @newsletter from @site', array(
    '@newsletter' => $newsletter->name,
    '@site' => $mail->from_name,
  ));
  $mail->body = t('This is a subscription status confirmation notice for the @newsletter.', array(
    '@newsletter' => $newsletter->name,
  ));
  $mail->body .= "\n\n";
  $user_is_subscribed = simplenews_user_is_subscribed($email, $newsletter->tid);
  switch ($op) {
    case 'subscribe':
      if ($user_is_subscribed) {
        $mail->body .= t('We have received a request for subscription of your e-mail address, @mail, to the @newsletter from @site (@uri). However, you are already subscribed to this newsletter. If you want to unsubscribe, you can visit our website by using the link at the bottom of this e-mail.', array(
          '@mail' => $email,
          '@newsletter' => $newsletter->name,
          '@site' => $mail->from_name,
          '@uri' => url('', NULL, NULL, TRUE),
        ));
      }
      else {
        $mail->body .= t('We have received a request for subscription of your e-mail address, @mail, to the @newsletter from @site (@uri). To confirm that you want to be added to this mailing list, simply visit the confirmation link at the bottom of this e-mail.', array(
          '@mail' => $email,
          '@newsletter' => $newsletter->name,
          '@site' => $mail->from_name,
          '@uri' => url('', NULL, NULL, TRUE),
        ));
        $mail->body .= "\n\n" . t('If you do not wish to be subscribed to this list, please disregard this message.');
        $footer = "\n\n-- \n" . t('Subscribe link: @url', array(
          '@url' => url('newsletter/confirm/add/' . $hash, NULL, NULL, TRUE),
        ));
      }
      break;
    case 'unsubscribe':
      if ($user_is_subscribed) {
        $mail->body .= t('We have received a request for the removal of your e-mail address, @mail, from the @newsletter from @site (@uri). If you want to unsubscribe, simply visit the confirmation link at the bottom of this e-mail.', array(
          '@mail' => $email,
          '@newsletter' => $newsletter->name,
          '@site' => $mail->from_name,
          '@uri' => url('', NULL, NULL, TRUE),
        ));
        $mail->body .= "\n\n" . t('If you did not make this request, please disregard this message.');
        $footer = "\n\n-- \n" . t('Unsubscribe link: @url', array(
          '@url' => url('newsletter/confirm/remove/' . $hash, NULL, NULL, TRUE),
        ));
      }
      else {
        $mail->body .= t('We have received a request for the removal of your e-mail address, @mail, from the @newsletter from @site (@uri). However, you were not subscribed to this newsletter. If you want to subscribe, you can visit our website by using the link at the bottom of this e-mail.', array(
          '@mail' => $email,
          '@newsletter' => $newsletter->name,
          '@site' => $mail->from_name,
          '@uri' => url('', NULL, NULL, TRUE),
        ));
        $mail->body .= "\n\n" . t('If you do not wish to be subscribed to this list, please disregard this message.');
      }
      break;
  }
  if (!isset($footer)) {
    $footer = "\n\n-- \n" . t('Visit our site: @url', array(
      '@url' => url('', NULL, NULL, TRUE),
    ));
  }
  $mail->body .= $footer;
  return $mail;
}

Functions

Namesort descending Description
simplenews_access Implementation of hook_access().
simplenews_activate_subscription Callback function to activate the specified subscriptions.
simplenews_admin_export_after_build
simplenews_admin_news
simplenews_admin_news_submit
simplenews_admin_settings Generate settings form.
simplenews_admin_settings_validate Forms API callback; validates the settings form.
simplenews_admin_types_delete Forms API callback; newsletter (term) delete form.
simplenews_admin_types_delete_submit Forms API callback; handles submit for newsletter (term) delete form.
simplenews_admin_types_form Forms API callback; displays newsletter (term) add/edit form.
simplenews_admin_types_form_submit Forms API callback; handles newsletter (term) form submit.
simplenews_admin_users_delete Forms API callback; delete user subscription form. Form to delete user from all newsletters.
simplenews_admin_users_delete_submit Forms API callback; delete user subscription submit.
simplenews_admin_users_form Menu callback; handles the edit/delete subscription page and a subscription page for anonymous users.
simplenews_block Implementation of hook_block().
simplenews_block_form Show block subscription form.
simplenews_block_form_submit Forms API callback; handles block form (un)subscribe submissions.
simplenews_block_form_validate Forms API callback; handles block form (un)subscribe validation.
simplenews_build_subscription_filter_query Build query for subscription filters based on session var content.
simplenews_confirm_add_form Generate the confirm add form.
simplenews_confirm_add_form_submit Forms API callback; handles form submission for a user confirming subscribe request.
simplenews_confirm_removal_form Generate the confirm remove form.
simplenews_confirm_removal_form_submit Forms API callback; handles form submission for a user confirming unsubscribe request.
simplenews_confirm_subscription Menu callback; confirm the user's (un)subscription request when they click on the confirm link in the e-mail footer.
simplenews_cron Implementation of hook_cron().
simplenews_delete Implementation of hook_delete().
simplenews_delete_subscription API function; deletes every subscription for the given subscription ID.
simplenews_form Implementation of hook_form().
simplenews_forms Implementation of hook_forms
simplenews_form_alter Implementation of hook_form_alter().
simplenews_get_filter Built filter selection box options and filter query where clause
simplenews_get_subscription API function; returns the subscription for the given subscription ID.
simplenews_get_user_subscription API function; returns the subscription for the given e-mail address.
simplenews_handle_messages
simplenews_help Implementation of hook_help
simplenews_html_to_text Convert html text to plain text.
simplenews_inactivate_subscription Callback function to inactivate the specified subscriptions.
simplenews_init Implementation of hook_init().
simplenews_insert Implementation of hook_insert(). Saves extra node content to module database table.
simplenews_load Implementation of hook_load().
simplenews_mail_confirm Send confirmation email
simplenews_mail_send Mail engine to send newsletter. If you want to send HTML newsletters you need to plug in an extra module
simplenews_menu Implementation of hook_menu().
simplenews_nodeapi Implementation of hook_nodeapi().
simplenews_node_info Implementation of hook_node_info().
simplenews_node_prepare Prepare node for sending
simplenews_perm Implementation of hook_perm().
simplenews_private_key
simplenews_recent_newsletters
simplenews_replace_vars
simplenews_send_test Send a test newsletter
simplenews_set_filter Store filter values in session var
simplenews_set_from Helper function to set from name and e-mail for a mail object.
simplenews_submit Implementation of hook_submit().
simplenews_subscribe_user API function; subscribes a user to a newsletter.
simplenews_subscription_admin Menu callback: subscription administration.
simplenews_subscription_filters Generate subscription filters
simplenews_subscription_filter_form Return form for subscription filters.
simplenews_subscription_filter_form_submit
simplenews_subscription_list_add
simplenews_subscription_list_add_submit
simplenews_subscription_list_add_validate
simplenews_subscription_list_export
simplenews_subscription_list_form
simplenews_subscription_list_form_submit Submit the subscription administration update form.
simplenews_subscription_list_form_validate
simplenews_subscription_manager_form Generates the subscription form for users.
simplenews_subscription_manager_form_submit Forms API callback; submit handler for subscription form.
simplenews_subscription_manager_form_validate Forms API callback; validates the settings form.
simplenews_subscription_multiple_delete_confirm Delete multiple subscriptions
simplenews_subscription_multiple_delete_confirm_submit
simplenews_taxonomy Implementation of hook_taxonomy().
simplenews_time
simplenews_types_overview
simplenews_unsubscribe_user API function; unsubscribes a user from a newsletter.
simplenews_update Implementation of hook_update().
simplenews_user Implementation of hook_user().
simplenews_user_is_subscribed API function; returns if the user's e-mail address is subscribed to the given newsletter.
simplenews_validate Implementation of hook_validate().
simplenews_validate_taxonomy
simplenews_view Implementation of hook_view().
theme_simplenews_block Simplenews themed block Access restrictions have been applied. $block contains all available data. See: _simplenews_block()
theme_simplenews_newsletter Theme the newsletter message subject and body.
theme_simplenews_newsletter_confirmation Construct the themable newsletter confirmation email.
theme_simplenews_newsletter_footer Theme the footer.
theme_simplenews_status
theme_simplenews_subscription_list_form Theme subscription administration overview.
_simplenews_block Helper function: generate block content before theming Collects data and applies access restriction
_simplenews_define_replace List of pattern replacements corresponding to patterns searched. Based on / modified from html2txt.module
_simplenews_define_search List of preg* regular expression patterns to search for, used in conjunction with $replace. Based on / modified from html2txt.module
_simplenews_format_options Helper function to determine possible mail format options. The mimemodule module must be installed to send HTML mails.
_simplenews_generate_hash Helper function to generate the hash key used for subscribe/unsubscribe link in e-mail footer.
_simplenews_mail_uri Format URL and link text for in-line display in plain text message.
_simplenews_mail_url Convert URL to absolute URL
_simplenews_mail_urls Collect and store URLs
_simplenews_send Send the newsletter
_simplenews_subscription_filter_default Helper function: returns subscription filter default settings
_simplenews_subscription_manager_form Helper function to build subscription manager form, also used in user edit.
_simplenews_user_load Helper function for API functions; loads a user or creates a dummy anonymous user with uid = 0 and $mail equal to the input param.