array( 'name' => t('Newsletter issue'), 'module' => 'simplenews', 'description' => t('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', 'delete own newsletter', 'delete any newsletter', 'administer newsletters', 'send newsletter', 'subscribe to newsletters'); } /** * Implementation of hook_access(). */ function simplenews_access($op, $node) { global $user; if ($op == 'create') { if (user_access('create newsletter')) { return TRUE; } } if ($op == 'update') { if (user_access('edit any newsletters')) { return TRUE; } elseif (user_access('edit own newsletter') && $user->uid == $node->uid) { return TRUE; } } if ($op == 'delete') { if (user_access('delete any newsletters')) { return TRUE; } elseif (user_access('delete own newsletter') && $user->uid == $node->uid) { return TRUE; } } } /** * Implementation of hook_init(). */ function simplenews_init() { drupal_add_css(drupal_get_path('module', 'simplenews') .'/simplenews.css', 'module', 'all', TRUE); if (module_exists('views')) { include_once(drupal_get_path('module', 'simplenews') .'/simplenews_views.inc'); } } /** * Implementation of hook_menu(). */ function simplenews_menu() { $items['admin/content/newsletters'] = array( 'title' => 'Newsletters', 'description' => 'Manage newsletters, subscriptions, and configuration options.', 'type' => MENU_NORMAL_ITEM, 'page callback' => 'drupal_get_form', 'page arguments' => array('simplenews_admin_news'), 'access arguments' => array('administer newsletters'), 'file' => 'simplenews.admin.inc', ); $items['admin/content/newsletters/sent'] = array( 'title' => 'Sent issues', 'type' => MENU_DEFAULT_LOCAL_TASK, 'page arguments' => array('simplenews_admin_news'), 'access arguments' => array('administer newsletters'), 'file' => 'simplenews.admin.inc', 'weight' => -10, ); $items['admin/content/newsletters/notsent'] = array( 'title' => 'Drafts', 'type' => MENU_LOCAL_TASK, 'page arguments' => array('simplenews_admin_news', 'notsent'), 'access arguments' => array('administer newsletters'), 'weight' => -9, ); $items['admin/content/newsletters/types'] = array( 'title' => 'Newsletters', 'type' => MENU_LOCAL_TASK, 'page callback' => 'simplenews_types_overview', 'access arguments' => array('administer newsletters'), 'file' => 'simplenews.admin.inc', 'weight' => -8, ); $items['admin/content/newsletters/types/edit/%'] = array( 'title' => 'Newsletters', 'type' => MENU_CALLBACK, 'page callback' => 'drupal_get_form', 'page arguments' => array('simplenews_admin_types_form', 5), 'access arguments' => array('administer newsletters'), 'file' => 'simplenews.admin.inc', ); $items['admin/content/newsletters/types/delete/%'] = array( 'title' => 'Newsletters', 'type' => MENU_CALLBACK, 'page callback' => 'drupal_get_form', 'page arguments' => array('simplenews_admin_types_delete', 5), 'access arguments' => array('administer newsletters'), 'file' => 'simplenews.admin.inc', ); $items['admin/content/newsletters/types/list'] = array( 'title' => 'List newsletters', 'type' => MENU_DEFAULT_LOCAL_TASK, 'file' => 'simplenews.admin.inc', 'weight' => -10, ); $items['admin/content/newsletters/types/add'] = array( 'title' => 'Add newsletter', 'type' => MENU_LOCAL_TASK, 'page callback' => 'drupal_get_form', 'page arguments' => array('simplenews_admin_types_form'), 'access arguments' => array('administer newsletters'), 'file' => 'simplenews.admin.inc', 'weight' => -9, ); $items['admin/content/newsletters/subscriptions/delete'] = array( 'title' => 'Delete', 'type' => MENU_CALLBACK, 'page callback' => 'drupal_get_form', 'page arguments' => array('simplenews_subscription_multiple_delete_confirm'), 'access arguments' => array('administer newsletters'), 'file' => 'simplenews.admin.inc', ); $items['admin/content/newsletters/users'] = array( 'title' => 'Subscriptions', 'type' => MENU_LOCAL_TASK, 'page callback' => 'drupal_get_form', 'page arguments' => array('simplenews_subscription_admin'), 'access arguments' => array('administer newsletters'), 'file' => 'simplenews.admin.inc', 'weight' => -7, ); $items['admin/content/newsletters/users/edit/%'] = array( 'title' => 'Subscriptions', 'type' => MENU_CALLBACK, 'page callback' => 'drupal_get_form', 'page arguments' => array('simplenews_admin_users_form', 5), 'access arguments' => array('administer newsletters'), 'file' => 'simplenews.subscription.inc', ); $items['admin/content/newsletters/users/list'] = array( 'title' => 'List subscriptions', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/content/newsletters/users/import'] = array( 'title' => 'Import subscriptions', 'type' => MENU_LOCAL_TASK, 'page callback' => 'drupal_get_form', 'page arguments' => array('simplenews_subscription_list_add'), 'access arguments' => array('administer newsletters'), 'file' => 'simplenews.admin.inc', 'weight' => -9, ); $items['admin/content/newsletters/users/export'] = array( 'title' => 'Export subscriptions', 'type' => MENU_LOCAL_TASK, 'page callback' => 'drupal_get_form', 'page arguments' => array('simplenews_subscription_list_export'), 'access arguments' => array('administer newsletters'), 'file' => 'simplenews.admin.inc', 'weight' => -8, ); $items['admin/content/newsletters/settings'] = array( 'title' => 'Settings', 'type' => MENU_LOCAL_TASK, 'page arguments' => array('simplenews_admin_settings'), 'access arguments' => array('administer newsletters'), 'weight' => -6, ); $items['admin/content/newsletters/settings/general'] = array( 'title' => 'General', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['newsletter/confirm'] = array( 'title' => 'Confirm newsletter subscriptions', 'type' => MENU_CALLBACK, 'page callback' => 'simplenews_confirm_subscription', 'access arguments' => array('subscribe to newsletters'), 'file' => 'simplenews.subscription.inc', ); $items['newsletter/subscriptions'] = array( 'title' => 'Manage newsletter subscriptions', 'type' => MENU_CALLBACK, 'page callback' => 'drupal_get_form', 'page arguments' => array('simplenews_subscription_manager_form'), 'access arguments' => array('subscribe to newsletters'), 'file' => 'simplenews.subscription.inc', ); return $items; } /** * Implementation of hook_nodeapi(). */ function simplenews_nodeapi(&$node, $op, $teaser, $page) { // We are going to return if $node->type is not one of the node // types assigned to the newsletter vocabulary. If simplenews_vid // is undefined or the vocabulary does not exist, it clearly cannot // be assigned to $node->type, so return to avoid E_ALL warnings. $vid = variable_get('simplenews_vid', ''); $vocabulary = taxonomy_vocabulary_load($vid); if (empty($vocabulary)) { return; } // Operate only on node types assigned to the newsletter vocabulary. if (!in_array($node->type, $vocabulary->nodes)) { return; } switch ($op) { case 'view': $node = simplenews_replace_vars($node, TRUE); break; case 'validate': global $_simplenews_valid_mails; if ($node->simplenews['send'] == SIMPLENEWS_COMMAND_SEND_NOW) { if (!isset($node->taxonomy)) { form_set_error('', t('You should assign content type %content_type to the !newsletter_vocabulary before you can send this newsletter.', array('%content_type' => $node->type, '!newsletter_vocabulary' => l(t('Newsletter vocabulary'), 'admin/content/taxonomy')))); } elseif (!simplenews_validate_taxonomy($node->taxonomy)) { form_set_error('taxonomy', t('You should select a newsletter before you can send this newsletter issue.')); } } elseif ($node->simplenews['send'] == SIMPLENEWS_COMMAND_SEND_TEST) { $mails = explode(',', $node->simplenews['test_address']); foreach ($mails as $mail) { $mail = trim($mail); if ($mail == '') { form_set_error('simplenews][test_address', t('Cannot send test newsletter: no test e-mail address specified.')); } elseif (!valid_email_address($mail)) { form_set_error('simplenews][test_address', t('Cannot send test newsletter to %mail: e-mail address invalid.', array('%mail' => $mail))); } } } break; case 'presave': $term = simplenews_validate_taxonomy($node->taxonomy); $tid = is_array($term) ? array_values($term) : FALSE; $node->simplenews['tid'] = $tid ? $tid[0] : 0; break; case 'insert': case 'update': $send_with_permission = $node->simplenews['send'] == SIMPLENEWS_COMMAND_SEND_NOW && user_access('send newsletter'); if ($op == 'insert') { // Insert node $s_status = $send_with_permission ? SIMPLENEWS_STATUS_SEND_PENDING : SIMPLENEWS_STATUS_SEND_NOT; db_query("INSERT INTO {simplenews_newsletters} (nid, vid, tid, s_status, s_format, priority, receipt) VALUES (%d, %d, %d, %d, '%s', '%s', %d)", $node->nid, $node->vid, $node->simplenews['tid'], $s_status, $node->simplenews['s_format'], $node->simplenews['priority'], $node->simplenews['receipt']); } else { // Update node if ($send_with_permission) { db_query("UPDATE {simplenews_newsletters} SET vid = %d, tid = %d, s_status = %d, s_format = '%s', priority = '%s', receipt = %d WHERE nid = %d", $node->vid, $node->simplenews['tid'], SIMPLENEWS_STATUS_SEND_PENDING, $node->simplenews['s_format'], $node->simplenews['priority'], $node->simplenews['receipt'], $node->nid); } else { db_query("UPDATE {simplenews_newsletters} SET tid = %d, s_format = '%s', priority = '%s', receipt = %d WHERE nid = %d", $node->simplenews['tid'], $node->simplenews['s_format'], $node->simplenews['priority'], $node->simplenews['receipt'], $node->nid); } } // When translation module is activated all translated modules share the send status //TODO Share all states, not only send state if (module_exists('translation') && $send_with_permission) { if ($translations = translation_node_get_translations($node->tnid)) { foreach ($translations as $translation) { db_query("UPDATE {simplenews_newsletters} SET s_status = %d WHERE nid = %d", SIMPLENEWS_STATUS_SEND_PENDING, $translation->nid); } } } // Send newsletter or test newsletter if ($send_with_permission) { // Send newsletter to all subscribers simplenews_send_node($node); } elseif ($node->simplenews['send'] == SIMPLENEWS_COMMAND_SEND_TEST) { // Send test newsletter to test address(es) simplenews_send_test($node); } break; case 'delete': $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))); } break; case 'load': $node->simplenews = db_fetch_array(db_query('SELECT * FROM {simplenews_newsletters} WHERE nid = %d', $node->nid)); break; } } /** * Implementation of hook_form(). */ function simplenews_form(&$node) { $type = node_get_types('type', $node); if ($type->has_title) { $form['title'] = array( '#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => $node->title, '#maxlength' => 255, '#weight' => -5, ); } if ($type->has_body) { $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count); // Available variables are based on user_mail_tokens(). // But uses only those which can be used with uid = 0 since simplenews also sends to anonymous users. $form['body_field']['body']['#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://),') . ' !date ' . t("(today's date),") . ' !login_uri ' . t('(link to login page).'); } return $form; } /** * Validate if term is Newsletter taxonomy term. * * @return * Array of selected Newsletter terms. Example: array(4, 12) * FALSE: no Newsletter term is selected */ 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_form_alter(). */ function simplenews_form_alter(&$form, $form_state, $form_id) { $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 newsletter vocabulary. $form['help_forum_vocab'] = array( '#value' => t('This is the designated simplenews vocabulary. This vocabulary can not be deleted.'), '#weight' => -1, ); $form['content_types']['nodes']['#required'] = TRUE; $form['content_types']['nodes']['#description'] = t('Select content type(s) to be used as newsletter'); $form['settings']['multiple'] = array('#type' => 'value', '#value' => FALSE); // Prevent the vocabulary gets deleted unset($form['delete']); } // Simplenews newsletter node form elseif (strpos($form_id, '_node_form') && isset($form['taxonomy'][$vid])) { if (isset($form['#node']->simplenews)) { $simplenews_values = $form['#node']->simplenews; } $form['simplenews'] = array( '#type' => 'fieldset', '#title' => t('Newsletter sending options'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#tree' => TRUE, ); // Hide format selection if there is nothing to choose if (count(_simplenews_format_options()) > 1) { $form['simplenews']['s_format'] = array( '#type' => 'select', '#title' => t('Format'), '#default_value' => $simplenews_values['s_format'], '#options' => _simplenews_format_options(), ); } else { $form['simplenews']['s_format'] = array( '#type' => 'hidden', '#value' => variable_get('simplenews_format', 'plain'), ); } $form['simplenews']['priority'] = array( '#type' => 'select', '#title' => t('Priority'), '#default_value' => isset($simplenews_values['priority']) ? $simplenews_values['priority'] : variable_get('simplenews_priority', SIMPLENEWS_PRIORITY_NONE), '#options' => array( SIMPLENEWS_PRIORITY_NONE => t('none'), SIMPLENEWS_PRIORITY_HIGHEST => t('highest'), SIMPLENEWS_PRIORITY_HIGH => t('high'), SIMPLENEWS_PRIORITY_NORMAL => t('normal'), SIMPLENEWS_PRIORITY_LOW => t('low'), SIMPLENEWS_PRIORITY_LOWEST => t('lowest'), ), ); $form['simplenews']['receipt'] = array( '#type' => 'checkbox', '#title' => t('Request receipt'), '#return_value' => 1, '#default_value' => isset($simplenews_values['receipt']) ? $simplenews_values['receipt'] : variable_get('simplenews_receipt', 0), ); if (!isset($simplenews_values['s_status']) || (isset($simplenews_values['s_status']) && $simplenews_values['s_status'] == SIMPLENEWS_STATUS_SEND_NOT)) { if (user_access('send newsletter')) { $options[SIMPLENEWS_COMMAND_SEND_NONE] = t("Don't send now"); $options[SIMPLENEWS_COMMAND_SEND_TEST] = t('Send one test newsletter to the test address'); $options[SIMPLENEWS_COMMAND_SEND_NOW] = t('Send newsletter'); $form['simplenews']['send'] = array( '#type' => 'radios', '#title' => t('Sending'), '#default_value' => isset($simplenews_values['send']) ? $simplenews_values['send'] : variable_get('simplenews_send', SIMPLENEWS_COMMAND_SEND_NONE), '#options' => $options, ); } else { $options[SIMPLENEWS_COMMAND_SEND_NONE] = t("Don't send now"); $options[SIMPLENEWS_COMMAND_SEND_TEST] = t('Send one test newsletter to the test address'); $form['simplenews']['send'] = array( '#type' => 'radios', '#title' => t('Sending'), '#default_value' => isset($simplenews_values['send']) ? $simplenews_values['send'] : SIMPLENEWS_COMMAND_SEND_NONE, '#options' => $options, '#description' => t('You have no privileges to send this newsletter'), ); } $address_default = variable_get('site_mail', ini_get('sendmail_from')); if (variable_get('simplenews_test_address_override', 0)) { $form['simplenews']['test_address'] = array( '#type' => 'textfield', '#title' => t('Test e-mail addresses'), '#description' => t('Supply a comma-separated list of e-mail addresses to be used as test addresses.'), '#default_value' => isset($simplenews_values['test_address']) ? $simplenews_values['test_address'] : variable_get('simplenews_test_address', $address_default), '#size' => 60, '#maxlength' => 128, ); } else { $form['simplenews']['test_address'] = array( '#type' => 'hidden', '#value' => variable_get('simplenews_test_address', $address_default), ); } } else { $form['simplenews']['none'] = array( '#type' => 'checkbox', '#title' => t('This newsletter has been sent'), '#return_value' => 0, '#attributes' => array('checked' => 'checked', 'disabled' => 'disabled'), ); } $form['simplenews']['s_status'] = array( '#type' => 'hidden', '#value' => isset($simplenews_values['s_status']) ? $simplenews_values['s_status'] : SIMPLENEWS_STATUS_SEND_NOT, ); } } /** * Implementation of hook_cron(). */ function simplenews_cron() { simplenews_mail_send(); } /** * Implementation of hook_taxonomy(). * * Deletes subscriptions to term when term is deleted, and cleans the blocks * table. */ function simplenews_taxonomy($op, $type, $term = NULL) { if ($op == 'delete' && $term['vid'] == variable_get('simplenews_vid', '')) { switch ($term) { case 'term': db_query('DELETE FROM {simplenews_snid_tid} WHERE tid = %d', $array['tid']); db_query("DELETE FROM {blocks} WHERE module = '%s' AND delta = '%s'", 'simplenews', $term['tid']); drupal_set_message(t('Deleted all subscriptions to newsletter %newsletter.', array('%newsletter' => $term['name']))); break; } } } /** * 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', TRUE)) { db_query("UPDATE {simplenews_subscriptions} SET activated = %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', TRUE)) { // Delete subscription and all newsletter subscriptions when account is removed $subscription = simplenews_get_user_subscription($account->mail); db_query('DELETE FROM {simplenews_snid_tid} WHERE snid = %d', $subscription->snid); db_query('DELETE FROM {simplenews_subscriptions} WHERE snid = %d', $subscription->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_subscription($account); $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')) { $account->content['simplenews'] = array( '#type' => 'user_profile_category', '#title' => t('Newsletters'), ); $tree = taxonomy_get_tree(variable_get('simplenews_vid', '')); foreach ($tree as $newsletter) { if (db_result(db_query('SELECT COUNT(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 (isset($subscriptions)) { $subscriptions = implode(', ', $subscriptions); } else { $subscriptions = t('None'); } $account->content['simplenews']['subscriptions'] = array( '#type' => 'user_profile_item', '#title' => t('Current subscriptions'), '#value' => $subscriptions, ); if (user_access('subscribe to newsletters')) { $account->content['simplenews']['my_newsletters'] = array( '#type' => 'user_profile_item', '#value' => t('Manage !my_subscriptions', array('!my_subscriptions' => l(t('my subscriptions'), 'user/'. $account->uid .'/edit/newsletter'))), '#weight' => -1, ); } return array(t('Newsletters') => $items); } break; } } /** * Implementation of hook_block(). */ function simplenews_block($op = 'list', $delta = 0, $edit = array()) { switch ($op) { case 'list': $blocks = array(); foreach (taxonomy_get_tree(variable_get('simplenews_vid', '')) as $newsletter) { //TODO: 1. without form -> by role; 2. with form -> user caching with refresh on subscribe/unsubscribe (option as setting) or no caching $blocks[$newsletter->tid] = array( 'info' => t('Newsletter: @title', array('@title' => $newsletter->name)), 'cache' => variable_get('simplenews_block_f_'. $newsletter->tid, 1) ? BLOCK_NO_CACHE : BLOCK_CACHE_PER_ROLE, ); } return $blocks; case 'configure': $form['simplenews_block_'. $delta]['simplenews_block_m_status_'. $delta] = array( '#type' => 'checkbox', '#title' => t('Display block message'), '#return_value' => 1, '#default_value' => variable_get('simplenews_block_m_status_'. $delta, 1), ); $form['simplenews_block_'. $delta]['simplenews_block_m_'. $delta] = array( '#type' => 'textfield', '#title' => t('Block message'), '#size' => 60, '#maxlength' => 128, '#default_value' => variable_get('simplenews_block_m_'. $delta, t('Stay informed on our latest news!')), ); $form['simplenews_block_'. $delta]['simplenews_block_f_'. $delta] = 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_'. $delta, 1), ); $form['simplenews_block_'. $delta]['simplenews_block_l_'. $delta] = array( '#type' => 'checkbox', '#title' => t('Display link to previous issues'), '#return_value' => 1, '#default_value' => variable_get('simplenews_block_l_'. $delta, 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_'. $delta] = array( '#type' => 'checkbox', '#title' => t('Display previous issues'), '#return_value' => 1, '#default_value' => variable_get('simplenews_block_i_status_'. $delta, 0), ); $form['simplenews_block_'. $delta]['simplenews_block_i_'. $delta] = 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_'. $delta, 5), ); $form['simplenews_block_'. $delta]['simplenews_block_r_'. $delta] = array( '#type' => 'checkbox', '#title' => t('Display RSS-feed icon'), '#return_value' => 1, '#default_value' => variable_get('simplenews_block_r_'. $delta, 1), ); return $form; case 'save': variable_set('simplenews_block_m_status_'. $delta, $edit['simplenews_block_m_status_'. $delta]); variable_set('simplenews_block_m_'. $delta, $edit['simplenews_block_m_'. $delta]); variable_set('simplenews_block_f_'. $delta, $edit['simplenews_block_f_'. $delta]); variable_set('simplenews_block_l_'. $delta, $edit['simplenews_block_l_'. $delta]); variable_set('simplenews_block_i_status_'. $delta, $edit['simplenews_block_i_status_'. $delta]); variable_set('simplenews_block_i_'. $delta, $edit['simplenews_block_i_'. $delta]); variable_set('simplenews_block_r_'. $delta, $edit['simplenews_block_r_'. $delta]); break; case 'view': if ($newsletter = (array) taxonomy_get_term($delta)) { $block = array( 'subject' => check_plain($newsletter['name']), 'content' => theme('simplenews_block', $newsletter['tid']), ); } return $block; } } /** * Load a user or creates a dummy anonymous user. * * @return account * object ( * mail, e-mail address * uid, uid or 0 for anonymous * ) */ 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 = $mail; } return $account; } /** * Subscribe a user to a newsletter or send a confirmation mail. * * The $confirm parameter determines the action: * FALSE = The user is subscribed * TRUE = User receives an email to verify the address and complete the subscription * A new subscription account is created when the user is subscribed to the first newsletter * * @param string $mail The e-mail address to subscribe to the newsletter. * @param integer $tid The term ID of the newsletter. * @param boolean $confirm TRUE = send confirmation mail; FALSE = subscribe immediate to the newsletter */ function simplenews_subscribe_user($mail, $tid, $confirm = TRUE) { //Prevent mismatches from accidental capitals in mail address $mail = strtolower($mail); $account = (object) array('mail' => $mail); $subscription = simplenews_get_subscription($account); // If user is not subscribed to ANY newsletter, create a subscription account if ($subscription->snid == 0) { // To subscribe a user: // 1. Fetch the users uid. // 2. Add the user to the database. // 3. Get the full subscription object based on the mail address. // Note that step 3 gets subscription data based on mail address because the uid can be 0 (for anonymous users) $account = _simplenews_user_load($mail); db_query("INSERT INTO {simplenews_subscriptions} (mail, uid, activated) VALUES ('%s', %d, %d)", $mail, $account->uid, 1); $subscription = simplenews_get_subscription($account); } if ($confirm) { // Send confirmation e-mail to user to complete subscription or to tell // them that he or she is already subscribed. // Confirmation mail is in the user preferred language which is by default the language_default(). $params['from'] = _simplenews_set_from(); $params['newsletter'] = taxonomy_get_term($tid); $params['context']['account'] = $subscription; drupal_mail('simplenews', 'subscribe', $mail, $subscription->language, $params); } elseif (!isset($subscription->tids[$tid])) { // OR 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; } /** * Unsubscribe a user from a newsletter or send a confirmation mail. * * The $confirm parameter determines the action: * FALSE = The user is unsubscribed * TRUE = User receives an email to verify the address and complete the unsubscription * The subscription account is deleted when the user is unsubscribed to the last newsletter * * @param string $mail The e-mail address to unsubscribe from the newsletter. * @param integer $tid The term ID of the newsletter. * @param boolean $confirm TRUE = send confirmation mail; FALSE = unsubscribe immediate from the newsletter */ function simplenews_unsubscribe_user($mail, $tid, $confirm = TRUE) { //Prevent mismatches from accidental capitals in mail address $mail = strtolower($mail); $account = (object) array('mail' => $mail); $subscription = simplenews_get_subscription($account); // The unlikely case that a user is subscribed from a non existing newsletter is logged if (!$newsletter = taxonomy_get_term($tid)) { watchdog('simplenews', 'Attempt to unsubscribe from non existing newsletter term ID %id', array('%id' => $tid), WATCHDOG_ERROR); return FALSE; } if ($confirm) { // Send confirmation e-mail to user to complete unsubscription // or to tell them that he or she is not subscribed // Confirmation mail is in the user preferred language. $params['from'] = _simplenews_set_from(); $params['newsletter'] = $newsletter; $params['context']['account'] = $subscription; drupal_mail('simplenews', 'unsubscribe', $mail, $subscription->language, $params); } elseif (isset($subscription->tids[$tid])) { // OR remove the user from the newsletter. db_query('DELETE FROM {simplenews_snid_tid} WHERE snid = %d AND tid = %d', $subscription->snid, $tid); // Clean up subscription account if user is not subscribed to any newsletter anymore if (!db_result(db_query("SELECT COUNT(*) FROM {simplenews_snid_tid} t WHERE t.snid = %d", $subscription->snid))) { db_query('DELETE FROM {simplenews_subscriptions} WHERE snid = %d', $subscription->snid); } } return TRUE; } /** * Check if the e-mail address is subscribed to the given newsletter. * * @param string $mail email address * @param integer $tid newsletter term id * * @return boolean TRUE = email address is subscribed to given newsletter term id */ function simplenews_user_is_subscribed($mail, $tid) { return db_result(db_query("SELECT COUNT(*) FROM {simplenews_subscriptions} s INNER JOIN {simplenews_snid_tid} t ON s.snid = t.snid WHERE s.mail = '%s' AND t.tid = %d", $mail, $tid)) ? TRUE : FALSE; } /** * Get the subscription object for the given account. * * Account is defined by (in order of preference) snid, e-mail address or uid. * If the account is not subscribed a default subscription object is returned * containing all available account info. * * @param object $account account details. Containing one or none of these items: * object( * snid : subscription id * mail : e-mail address * uid : user id * ) * * @return subscription object * object( * snid : subscription id. 0 if account is not subscribed * tids : array of newsletter tid's * uid : user id. 0 if account is anonymous user * mail : user e-mail address. empty if e-mail is unknown * name : always empty. Added for compatebility with user account object * language : language object. User preferred or default language * ) */ function simplenews_get_subscription($account) { // Load subscription data based on available account informatioin // NOTE that the order of checking for snid, mail and uid is critical. mail must be checked *before* uid. See simplenews_subscribe_user() if (isset($account->snid)) { $subscription = db_fetch_object(db_query("SELECT s.*, u.language FROM {simplenews_subscriptions} s INNER JOIN {users} u ON u.uid = s.uid WHERE s.snid = %d", $account->snid)); } elseif (isset($account->mail)) { $subscription = db_fetch_object(db_query("SELECT s.*, u.language FROM {simplenews_subscriptions} s INNER JOIN {users} u ON u.uid = s.uid WHERE s.mail = '%s'", $account->mail)); } elseif (isset($account->uid)) { $subscription = db_fetch_object(db_query("SELECT s.*, u.language FROM {simplenews_subscriptions} s INNER JOIN {users} u ON u.uid = s.uid WHERE s.uid = %d", $account->uid)); } 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; } $subscription->name = ''; $subscription->language = user_preferred_language($subscription, language_default()); } else { // Account is unknown in subscription table. Create default subscription object $subscription = new stdClass(); $subscription->name = ''; $subscription->uid = isset($account->uid) ? $account->uid : 0; $subscription->mail = isset($account->mail) ? $account->mail : ''; $subscription->language = language_default(); $subscription->snid = 0; $subscription->tids = array(); } return $subscription; } /** * Delete every subscription for the given subscription ID. * * @param integer $snid 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('simplenews', 'User %email deleted from the mailing list.', array('%email' => $account->mail), WATCHDOG_NOTICE); } /** * Build subscription manager form. * * @param object $subscription subscription object */ function _simplenews_subscription_manager_form($subscription) { $form = array(); $options = array(); foreach (taxonomy_get_tree(variable_get('simplenews_vid', '')) as $newsletter) { $options[$newsletter->tid] = $newsletter->name; $default_value[$newsletter->tid] = FALSE; } $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' => array_merge($default_value, (array)$subscription->tids), ); // If current user is logged in, just display e-mail. // Anonymous users see an e-mail box and will receive confirmations if ($subscription->mail) { $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; } /** * Create a list of recent newsletters. * * @param integer $tid term id of selected newsletter * @param integer $count number of newsletters in the list */ function simplenews_recent_newsletters($tid, $count = 5) { $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.vid = t.vid INNER JOIN {simplenews_newsletters} sn ON n.nid = sn.nid WHERE (t.tid = %d AND n.status = %d) ORDER BY n.created DESC'), $tid, 1, 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 == SIMPLENEWS_STATUS_SEND_NOT) ? 'newsletter-created' : 'newsletter-send'; } return $titles; } /** * Newsletter (un)subscription form for authenticated and anonymous users. * * @param $tid term id of selected newsletter. * * @see simplenews_block_form_validate() * @see simplenews_block_form_submit() */ function simplenews_block_form(&$form_state, $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( '#type' => 'item', '#title' => t('User'), '#value' => check_plain($user->name), ); $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')), ); } $form['#tid'] = $tid; $form['submit'] = array( '#type' => 'submit', '#value' => isset($submit_text) ? $submit_text : t('Save') ); return $form; } function simplenews_block_form_validate($form, &$form_state) { if (!valid_email_address($form_state['values']['mail'])) { form_set_error('mail', t("The e-mail address you supplied is not valid.")); } } function simplenews_block_form_submit($form, &$form_state) { global $user; $tid = $form['#tid']; $account = _simplenews_user_load($form_state['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_state['values']['action']) { case 'subscribe': simplenews_subscribe_user($form_state['values']['mail'], $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_state['values']['mail'], $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; } } /** * Send newsletter node to subcribers. * * @param integer or object $node Newsletter node to be send. integer = nid; object = node object * @param array $accounts account objects to send the newsletter to. * account = object ( * snid = subscription id. 0 if no subscription record exists * tids = array(tid) array of newsletter tid's * uid = user id. 0 if subscriber is anonymous user. * mail = user e-mail address. * name = . Added for compatebility with user account object * language = language object. User preferred of default language * ) * NOTE: either snid, mail or uid is required. */ function simplenews_send_node($node, $accounts = array()) { if (is_numeric($node)) { $node = node_load($node); } if (is_object($node)) { $from = _simplenews_set_from($node); $params['context']['node'] = $node; $params['from'] = $from; if (empty($accounts)) { // No accounts specified. Send message to all accounts subscribed to this newsletter. // Account data is taken directly from simplenews_subscriptions table. simplenews_get_subscription() is not used because it is not (yet) optimized for multiple subscribers. // Name and language are added because this data is not (yet) in the table. $tid = $node->simplenews['tid']; $result = db_query('SELECT s.mail, s.snid, u.language FROM {simplenews_subscriptions} s INNER JOIN {simplenews_snid_tid} t ON s.snid = t.snid INNER JOIN {users} u ON s.uid = u.uid WHERE s.activated = %d AND t.tid = %d ORDER BY s.snid ASC', 1, $tid); while ($account = db_fetch_object($result)) { // Add values to account which currently do not exist in the simplenews_subscription table to match the $user account object $account->name = ''; $account->language = user_preferred_language($account); $accounts[] = $account; } } else { // Send newsletter to specified accounts. // Account data is gathered based on the supplied uid or email address. foreach ($accounts as $account) { $subscriptions[] = simplenews_get_subscription($account); } $accounts = $subscriptions; } // Mail is send directly (simplenews_use_cron = FALSE) or stored in a mail cache (simplenews_use_cron = TRUE) // Cached emails are send by simplenews_mail_send() on each cron run foreach ($accounts as $account) { $params['context']['account'] = $account; $message = drupal_mail('simplenews', 'node', $account->mail, $account->language, $params, $from['formatted'], !variable_get('simplenews_use_cron', TRUE)); if (variable_get('simplenews_use_cron', TRUE)) { // Chache newsletter mail messages simplenews_mail_cache_set($message); } else { if (variable_get('simplenews_debug', FALSE)) { watchdog('simplenews', 'Outgoing email. Message type: %type
Subject: %subject
Recipient: %to', array('%type' => $key, '%to' => $message['to'], '%subject' => $message['subject']), WATCHDOG_DEBUG); } } } } } /** * Send test version of newsletter. * * @param integer or object $node Newsletter node to be send. Integer = nid; Object = node object * @param array $accounts account objects to send the test newsletter to. * account = object( * mail : e-mail address of recipient * ) */ function simplenews_send_test($node, $accounts = array()) { if (is_numeric($node)) { $node = node_load($node); } if (is_object($node)) { $from = _simplenews_set_from($node); $params['context']['node'] = $node; $params['from'] = $from; // If no account is specified the test newsletter is send to the test address(es) specified in the node. if (empty($accounts)) { // Build array of test e-mail addresses $mails = explode(',', $node->simplenews['test_address']); foreach ($mails as $mail) { $account['mail'] = trim($mail); $accounts[] = (object)$account; } } // Test emails are send directly by drupal_mail(). This in contrast to simplenews_send_node() which send via a buffer. foreach ($accounts as $account) { $subscription = simplenews_get_subscription($account); $params['context']['account'] = $subscription; $result = drupal_mail('simplenews', 'test', $account->mail, $subscription->language, $params, $from['formatted']); if ($result['result']) { drupal_set_message(t('Test newsletter sent to %recipient.', array('%recipient' => $account->mail))); } } } } /** * Implementation of hook_mail. * * Send simplenews mails using drupal mail API * @see drupal_mail() * * @param $key: node | test | subscribe | unsubscribe * @param array $message message array * [from] * [headers][From] * @param array $params parameter array * [context][node] : node object of message to be send * [context][snid] : used for $key = subscribe or unsubscribe * [context][from_name] : name of mail sender or site name (optional) * [context][account] : account details of recipient * [from] : array('address' => 'noreply@example.org', 'formatted' => 'site name ') * [newsletter] : newsletter object (tid, name) * [tokens] : tokens for variable replacement. Defaults to: user_mail_tokens() */ function simplenews_mail($key, &$message, $params) { $context = $params['context']; switch ($key) { case 'node': case 'test': // Message header, body and mail headers are buffered to increase // perfomance when sending multiple mails. Buffered data only contains // general data, no recipient specific content. Placeholders are used // for recipient data and will later be replaced. // When mailing multiple newsletters in one page call or cron run, // data is once stored and subsequently retreived from the // static $messages variable. // $message buffer is node and language specific. static $messages = array(); $nid = $context['node']->nid; $langcode = $message['language']->language; if (module_exists('translation')) { // If the node has translations and a translation is required // the equivalent of the node in the required langugage is used // or the base node (tnid) is used if ($tnid = $context['node']->tnid) { if ($langcode != $context['node']->language) { $translations = translation_node_get_translations($tnid); if ($translation = $translations[$langcode]) { $nid = $translation->nid; $langcode = $translation->language; } else { // No translation found which matches the required language // Base node ($tnid) is used. The matching language replaces // the required language. foreach ($translations as $translation) { if ($translation->nid == $tnid) { $nid = $tnid; $langcode = $translation->language; break; } } } } } // If a different node is selected and this node is not available in // the message buffer, the selected node load will replace the original. if ($nid != $context['node']->nid && !isset($messages[$nid][$langcode])) { $context['node'] = node_load($nid); } } if (!isset($messages[$nid][$langcode])) { $node = $context['node']; // Add simplenews specific header data $headers = array_merge($message['headers'], _simplenews_headers($node, $params['from']['address'])); $message['headers'] = $messages[$nid][$langcode]['headers'] = $headers; // Build message subject if ($tid = $node->simplenews['tid']) { $term = taxonomy_get_term($tid); $name = check_plain($term->name); } else { $name = t('Unassigned newsletter'); } $subject = theme('simplenews_newsletter_subject', $name, check_plain($node->title), $langcode); $subject = str_replace(array("\r", "\n"), '', $subject); $message['subject'] = $messages[$nid][$langcode]['subject'] = $subject; // Build message body // Processing node body mimics node_view() with full 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 = node_prepare($node); $body = theme('simplenews_newsletter_body', $node->body, check_plain($node->title), $langcode); // Buffer body text node and language specific $messages[$nid][$langcode]['body'] = $body; } else { // Retreived headers, subject and raw body from the buffer $message['headers'] = $messages[$nid][$langcode]['headers']; $message['subject'] = $messages[$nid][$langcode]['subject']; $body = $messages[$nid][$langcode]['body']; } $variables = user_mail_tokens($context['account'], $context['account']->language); $body = strtr($body, $variables); $body = simplenews_html_to_text($body); $message['body']['body'] = $body; // Build message footer if ($key == 'node' && isset($context['account']->snid)) { $hash = _simplenews_generate_hash($context['account']->mail, $context['account']->snid, $node->newsletter['tid']); } else { $hash = ''; } $message['headers']['List-Unsubscribe'] = '<'. url('newsletter/confirm/remove/'. $hash, array('absolute' => TRUE)) .'>'; $message['body']['footer'] = theme('simplenews_newsletter_footer', $node->simplenews['s_format'], $hash, $key == 'test', $langcode); break; case 'subscribe': case 'unsubscribe': if (isset($context['account']->snid) && isset($params['newsletter']->tid)) { $hash = _simplenews_generate_hash($context['account']->mail, $context['account']->snid, $params['newsletter']->tid); } else { $hash = NULL; } // Replace default site address with formatted simplenews from address $message['headers']['From'] = $message['headers']['Reply-To'] = $params['from']['formatted']; //TODO: Too much logic in this theme function! Split it or move the text here without theming. Additional variable replacement needed. $langcode = $context['account']->language->language; $subject = theme('simplenews_newsletter_confirmation_subject', $params['newsletter'], variable_get('simplenews_from_name', $name_default), $langcode); $message['subject'] = str_replace(array("\r", "\n"), '', $subject); $message['body'] = theme('simplenews_newsletter_confirmation_body', $context['account']->mail, $params['newsletter'], $key, $hash, $langcode); break; } // Debug message to check for outgoing emails messages. // Newsletters ($key = 'node') are not send now. Debugging will take place in simplenews_mail_send() when the newsletters are send if (variable_get('simplenews_debug', FALSE) && $key != 'node') { watchdog('simplenews', 'Outgoing email. Message type: %type
Subject: %subject
Recipient: %to', array('%type' => $key, '%to' => $message['to'], '%subject' => $message['subject']), WATCHDOG_DEBUG); } } /** * Mailing 'backend' for simplenews newsletters. * * Newsletters are formatted and buffered per recipient in database cache. * Sending is initiated by cron * Messages are retreived from the database cache and send one by one * If sending is succesful the message is removed from the database cache. */ function simplenews_mail_send() { // Send pending messages from database cache // A limited number of mails is send each time simplenews_mail_send() called if ($messages = simplenews_mail_cache_get(SIMPLENEWS_CACHE_PENDING, variable_get('simplenews_throttle', 20))) { // The id of succesfully sent messages are stored. // At completion these messages are removed from the mail cache table. $mail_sent = array(); foreach($messages as $key => $message) { $counter++; if (variable_get('simplenews_debug', FALSE)) { watchdog('simplenews', 'Outgoing email. Message type: %type
Subject: %subject
Recipient: %to', array('%type' => $key, '%to' => $message['to'], '%subject' => $message['subject']), WATCHDOG_DEBUG); } if (drupal_mail_send($message)) { $mail_sent[] = $key; } else { watchdog('error', 'Error sending newsletter to %recipient.', array('%recipient' => $message['to']), WATCHDOG_ERROR); } } // All succesfully sent messages are removed from the mail cache. simplenews_mail_cache_clear($mail_sent); } } /** * Store mail message in mail cache table. * * @param array $message Mail message array */ function simplenews_mail_cache_set($message) { $tid = $message['params']['context']['node']->simplenews['tid']; $nid = $message['params']['context']['node']->nid; unset($message['params']); db_query("INSERT INTO {simplenews_mail_cache} (nid, tid, subject, mail, status, timestamp, message) VALUES (%d, %d, '%s', '%s', %d, %d, %b)", $nid, $tid, $message['subject'], $message['to'], SIMPLENEWS_CACHE_PENDING, time(), serialize($message)); } /** * Retreive data from mail cache table * * @param string $status Status of data to be retreived (0 = hold, 1 = pending, 2 = send) * @param integer $limit the maximum number of mails loaded from the cache * * @return array Mail message array */ function simplenews_mail_cache_get($status, $limit = 999999) { $result = db_query("SELECT c.mcid, c.message FROM {simplenews_mail_cache} c WHERE c.status = %d ORDER BY c.timestamp ASC LIMIT 0, %d", $status, $limit); while ($data = db_fetch_object($result)) { $messages[$data->mcid] = unserialize($data->message); } return $messages; } /** * Count data in mail cache table. * * @param integer $nid newsletter node id * @param string $status email send status * * @return array Mail message array */ function simplenews_mail_cache_count($nid, $status = SIMPLENEWS_CACHE_PENDING) { return db_result(db_query("SELECT COUNT(*) FROM {simplenews_mail_cache} c WHERE c.nid = %d AND c.status = %d", $nid, $status)); } /** * Update status of mail data in cache table. * * Time stamp is set to current time. * * @param array $array Mail cache id of record to be updated * @param string $status New status (0 = hold, 1 = pending, 2 = send) */ function simplenews_mail_cache_update($array, $status) { db_query("UPDATE {simplenews_mail_cache} SET status = %d, timestamp = %d WHERE mcid IN (%s)", $status, time(), $implode(',', $array)); } /** * Remove records from mail cache table. * * @param array $array Mail cache ids of records to be deleted */ function simplenews_mail_cache_clear($array) { $result = db_query("DELETE FROM {simplenews_mail_cache} WHERE mcid IN (%s)", implode(',', $array)); } /** * Build formatted from-name and e-mail for a mail object. * * Each newsletter (serie; tid) can have a different from address. * The from name and address depend on the newsletter term tid which is included in the $node object * * @param object $node Node object of a simplenews newsletter * * @return array [address] = from address; [formatted] = formatted from name and address */ function _simplenews_set_from($node = NULL) { $address_default = variable_get('site_mail', ini_get('sendmail_from')); $name_default = variable_get('site_name', 'Drupal'); if (isset($node->simplenews['tid'])) { $address = variable_get('simplenews_from_address_'. $node->simplenews['tid'], $address_default); $name = variable_get('simplenews_from_name_'. $node->simplenews['tid'], $name_default); } else { $address = variable_get('simplenews_from_address', $address_default); $name = variable_get('simplenews_from_name', $name_default); } return array( 'address' => $address, 'formatted' => '"'. mime_header_encode(addslashes(check_plain($name))) .'" <'. $address .'>', ); } /** * Build header array with priority and receipt confirmation settings. * * @param $node: node object * @param $from: from e-mail address * * @return Header array with priority and receipt confirmation info */ function _simplenews_headers($node, $from) { $headers = array(); // If receipt is requested, add headers. if ($node->simplenews['receipt']) { $headers['Disposition-Notification-To'] = $from; $headers['X-Confirm-Reading-To'] = $from; } // Add priority if set. switch ($node->simplenews['priority']) { case SIMPLENEWS_PRIORITY_HIGHEST: $headers['Priority'] = 'High'; $headers['X-Priority'] = '1'; $headers['X-MSMail-Priority'] = 'Highest'; break; case SIMPLENEWS_PRIORITY_HIGH: $headers['Priority'] = 'urgent'; $headers['X-Priority'] = '2'; $headers['X-MSMail-Priority'] = 'High'; break; case SIMPLENEWS_PRIORITY_NORMAL: $headers['Priority'] = 'normal'; $headers['X-Priority'] = '3'; $headers['X-MSMail-Priority'] = 'Normal'; break; case SIMPLENEWS_PRIORITY_LOW: $headers['Priority'] = 'non-urgent'; $headers['X-Priority'] = '4'; $headers['X-MSMail-Priority'] = 'Low'; break; case SIMPLENEWS_PRIORITY_LOWEST: $headers['Priority'] = 'non-urgent'; $headers['X-Priority'] = '5'; $headers['X-MSMail-Priority'] = 'Lowest'; break; } // Add general headers $headers['Precedence'] = 'bulk'; return $headers; } /** * HTML to text conversion for HTML and special characters. * * Converts some special HTMLcharacters in addition to drupal_html_to_text() * * @param string $text Source text with HTML and special characters * * @return string Target text with HTML and special characters replaced */ function simplenews_html_to_text($text) { // Remove in-page links $pattern = '@]+?href="#[^"]*"[^>]*?>(.+?)@i'; $text = preg_replace($pattern, '$1', $text); // Replace some special characters before performing the drupal standard conversion $preg = _simplenews_html_replace(); $text = preg_replace(array_keys($preg), array_values($preg), $text); // Perform standard drupal html to text conversion return drupal_html_to_text($text); } /** * List of preg* regular expression patterns to search for and replace with */ function _simplenews_html_replace() { return array( '/"/i' => '"', '/>/i' => '>', '/</i' => '<', '/&/i' => '&', '/©/i' => '(c)', '/™/i' => '(tm)', '/“/' => '"', '/”/' => '"', '/–/' => '-', '/’/' => "'", '/&/' => '&', '/©/' => '(c)', '/™/' => '(tm)', '/—/' => '--', '/“/' => '"', '/”/' => '"', '/•/' => '*', '/®/i' => '(R)', '/•/i' => '*', '/€/i' => 'Euro ', ); } /** * Replace tokens in node body/teaser with user specific variables. */ function simplenews_replace_vars($node, $teaser = TRUE) { global $user; $variables = user_mail_tokens($user, $user->language); $node->body = strtr($node->body, $variables); if ($teaser) { $node->teaser = strtr($node->teaser, $variables); } return $node; } /** * Create a 32 character identifier. */ 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($path, $arg) { switch ($path) { case 'admin/help#simplenews': $help = "

". t('Simplenews sends html or plain text newsletters to the mailing list. Subscription and unsubscription are managed through a block, a form or by the newsletter administrator.') ."
\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.') ."
\n"; $help .= t('Sending of large mailings can be managed by cron.') ."

\n"; $help .= "

". t('For more information please read the configuration and customization handbook Simplenews page.', array('@handbook','http://drupal.org/node/197057')) ."

\n"; return $help; case 'node/add/simplenews': $help = "

". t('Add this newsletter issue to a newsletter by selecting a newsletter from the select list.') ."

\n"; $help .= "

". t('Send a newsletter or a test newsletter by selecting the appropriate radio button and submitting the node.') ."

\n"; if (!module_exists('mimemail')) { $help .= "

". 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.') ."

\n"; } return $help; case 'admin/content/newsletters/settings': $help = '

'. t('Newsletter specific settings can be found at the Newsletters settings page.', array('@page' => url('admin/content/newsletters/types'))) ."

\n"; return $help; } } /** * Generate the hash key used for subscribe/unsubscribe link. */ function _simplenews_generate_hash($mail, $snid, $tid) { return drupal_substr(md5($mail . simplenews_private_key()), 0, 10) . $snid .'t'. $tid; } /** * Determine possible mail format options. * * The mime_mail 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; } /** * Implementation of hook_theme. */ function simplenews_theme() { return array( 'simplenews_block' => array( 'template' => 'simplenews-block', 'arguments' => array('tid' => NULL), ), 'simplenews_status' => array( 'template' => 'simplenews-status', 'file' => 'simplenews.admin.inc', 'arguments' => array('status' => NULL, 'source' => NULL), ), 'simplenews_newsletter' => array( 'arguments' => array('node' => NULL, 'tid' => NULL), ), 'simplenews_newsletter_subject' => array( 'arguments' => array('name' => NULL, 'title' => NULL, 'langcode' => NULL), ), 'simplenews_newsletter_body' => array( 'arguments' => array('body' => NULL, 'title' => NULL, 'langcode' => NULL), ), 'simplenews_newsletter_footer' => array( 'arguments' => array('format' => NULL, 'hash' => NULL, 'test' => NULL, 'langcode' => NULL), ), 'simplenews_newsletter_confirmation_subject' => array( 'arguments' => array('newsletter' => NULL, 'site_name' => NULL, 'langcode' => NULL), ), 'simplenews_newsletter_confirmation_body' => array( 'arguments' => array('email' => NULL, 'newsletter' => NULL, 'op' => NULL, 'hash' => NULL, 'langcode' => NULL) ), 'simplenews_newsletter_confirmation' => array( 'arguments' => array('email' => NULL, 'newsletter' => NULL, 'snid' => NULL, 'op' => NULL, 'hash' => NULL), ), 'simplenews_subscription_list' => array( 'file' => 'simplenews.admin.inc', 'arguments' => array('form' => NULL), ), ); } /** * Process variables to format the simplenews block. * * Collect data and apply access restrictions. * * $variables contains: * - $tid * * @see simplenews-block.tpl.php * @see theme_simplenews-block() */ function template_preprocess_simplenews_block(&$variables) { global $user; $tid = $variables['tid']; // Block content variables $variables['message'] = check_plain(variable_get('simplenews_block_m_'. $tid, t('Stay informed on our latest news!'))); if (user_access('subscribe to newsletters')) { $variables['form'] = drupal_get_form('simplenews_block_form', $tid); $variables['subscription_link'] = l(t('Manage my subscriptions'), 'newsletter/subscriptions'); } if (user_access('view links in block') || user_access('administer newsletters')) { $variables['newsletter_link'] = l(t('Previous issues'), 'taxonomy/term/'. $tid); $recent = simplenews_recent_newsletters($tid, variable_get('simplenews_block_i_'. $tid, 5)); $variables['issue_list'] = theme('item_list', $recent, t('Previous issues'), 'ul'); $term = taxonomy_get_term($tid); $variables['rssfeed'] = theme('feed_icon', url('taxonomy/term/'. $tid .'/0/feed'), t('@newsletter feed', array('@newsletter' => $term->name))); } // Block content control variables $variables['use_message'] = variable_get('simplenews_block_m_status_'. $tid, 1); $variables['use_form'] = variable_get('simplenews_block_f_'. $tid, 1); $variables['use_issue_link'] = variable_get('simplenews_block_l_'. $tid, 1); $variables['use_issue_list'] = variable_get('simplenews_block_i_status_'. $tid, 0); $variables['use_rss'] = variable_get('simplenews_block_r_'. $tid, 1); // Additional variables $variables['subscribed'] = (simplenews_user_is_subscribed($user->mail, $tid) == TRUE); $variables['user'] = !empty($user->uid); } /** * Theme the newsletter message subject and body. */ function theme_simplenews_newsletter($node, $tid) { $term = taxonomy_get_term($tid); $name = $term->name ? $term->name : t('Unassigned newsletter'); $node->subject = '['. $name .'] '. $node->title; $node->body = '

'. $node->title ."

\n". $node->body; return $node; } /** * Theme the newsletter e-mail subject. */ function theme_simplenews_newsletter_subject($name, $title, $langcode) { return '['. $name .'] '. $title; } /** * Theme the newsletter message body. */ function theme_simplenews_newsletter_body($body, $title, $langcode) { $output = '

'. $title ."

\n"; $output .= $body; return $output; } /** * Theme the newsletter message footer. */ function theme_simplenews_newsletter_footer($format, $hash, $test = FALSE, $langcode) { if ($format == 'html') { $output = '

--
'. l(t('Click here to unsubscribe from this newsletter'), 'newsletter/confirm/remove/'. $hash, array('html' => TRUE), $langcode) .'

'; } else { $output = "\n\n-- \n". t('Unsubscribe from this newsletter: !url', array('!url' => url('newsletter/confirm/remove/'. $hash, array('absolute' => TRUE))), $langcode); } if ($test) { $output .= "\n- - - ". t('This is a test version of the newsletter. The above unsubscribe link does not work!', array(), $langcode) .' - - -'; } return $output; } function theme_simplenews_newsletter_confirmation_subject($newsletter, $site_name, $langcode) { return t('Confirmation for @newsletter from @site', array('@newsletter' => $newsletter->name, '@site' => $site_name), $langcode); } /** * Construct the themable newsletter confirmation email. */ function theme_simplenews_newsletter_confirmation_body($email, $newsletter, $op, $hash, $langcode) { $body[] = t('This is a subscription status confirmation notice for the @newsletter.', array('@newsletter' => $newsletter->name), $langcode); $body[] = "\n\n"; $user_is_subscribed = simplenews_user_is_subscribed($email, $newsletter->tid); switch ($op) { case 'subscribe': if ($user_is_subscribed) { $body[] = t('We have received a request for subscription of your e-mail address, @mail, to the @newsletter from @site (!url). 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, '!url' => url('', array('absolute' => TRUE))), $langcode); $body[] = "\n\n-- \n". t('Visit our site: !url', array('!url' => url('', array('absolute' => TRUE))), $langcode); } else { $body[] = t('We have received a request for subscription of your e-mail address, @mail, to the @newsletter from @site (!url). 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, '!url' => url('', array('absolute' => TRUE))), $langcode); $body[] = "\n\n". t('If you do not wish to be subscribed to this list, please disregard this message.', array(), $langcode); $body[] = "\n\n-- \n". t('Subscribe link: !url', array('!url' => url('newsletter/confirm/add/'. $hash, array('absolute' => TRUE))), $langcode); } break; case 'unsubscribe': if ($user_is_subscribed) { $body[] = t('We have received a request for the removal of your e-mail address, @mail, from the @newsletter from @site (!url). 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, '!url' => url('', array('absolute' => TRUE))), $langcode); $body[] = "\n\n". t('If you did not make this request, please disregard this message.', array(), $langcode); $body[] = "\n\n-- \n". t('Unsubscribe link: !url', array('!url' => url('newsletter/confirm/remove/'. $hash, array('absolute' => TRUE))), $langcode); } else { $body[] = t('We have received a request for the removal of your e-mail address, @mail, from the @newsletter from @site (!url). 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, '!url' => url('', array('absolute' => TRUE))), $langcode); $body[] = "\n\n". t('If you do not wish to be subscribed to this list, please disregard this message.', array(), $langcode); $body[] = "\n\n-- \n". t('Visit our site: !url', array('!url' => url('', array('absolute' => TRUE))), $langcode); } break; } return $body; } /** * Construct the themable newsletter confirmation email. */ function theme_simplenews_newsletter_confirmation($email, $newsletter, $snid, $op, $hash) { $mail = new stdClass(); $mail->simplenews['s_format'] = 'plain'; $mail->simplenews['priority'] = 'none'; $mail->simplenews['to'] = $email; $mail->simplenews['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 (!url). 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, '!url' => url('', array('absolute' => TRUE)))); } else { $mail->body .= t('We have received a request for subscription of your e-mail address, @mail, to the @newsletter from @site (!url). 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, '!url' => url('', array('absolute' => 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, array('absolute' => 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 (!url). 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, '!url' => url('', array('absolute' => 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, array('absolute' => TRUE)))); } else { $mail->body .= t('We have received a request for the removal of your e-mail address, @mail, from the @newsletter from @site (!url). 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, '!url' => url('', array('absolute' => 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('', array('absolute' => TRUE)))); } $mail->body .= $footer; return $mail; }