' . t('This module provides a block with a simple newsletter subscription form, i.e., an text input field (validated as an e-mail address) and a submit button. In the block configuration page you can add a header, a label and a footer to the form, as well as a small text appearing in the input field(usually used as a replacement to the field label. You can also choose a path to redirect the user after a successful submission.', array('@block_configuration' => url('admin/structure/block/configure/simple_subscription/0'))) . '

'; $output .= '

' . t("Simple subscription integrates with the trigger module to send site administrators (or anybody else) the submitted. It can be used with only the core action module for notifications, but the Token module should also be installed for complete costumisation of the notification e-mails.", array('@token_url' => 'http://drupal.org/project/token')) . '

'; break; } return $output; } /** * Implements hook_permission(). */ function simple_subscription_permission() { return array( 'manage own subscription' => array( 'title' => t("Manage own subscription to the newsletter."), ), 'manage all subscriptions' => array( 'title' => t("Manage subscriptions of all users."), 'restrict access' => TRUE, ), 'administer simple subscription' => array( 'title' => t("Administer Simple Subscription"), 'description' => t('Allow users to change Simple Subscription settings.') ), ); } /** * Implements hook_entity_info(). */ function simple_subscription_entity_info() { $entity['subscription'] = array( 'label' => t('subscription'), 'base table' => 'simple_subscription_subscriptions', 'fieldable' => FALSE, 'entity keys' => array( 'id' => 'sid' ), ); return $entity; } /** * Implements hook_menu(). */ function simple_subscription_menu() { $items = array(); $items['admin/config/simple_subscription'] = array( 'title' => "Simple Subscription", 'access arguments' => array('administer simple subscription'), 'page callback' => 'drupal_get_form', 'page arguments' => array('simple_subscription_main_admin_form'), 'file' => 'simple_subscription.pages.inc', 'type' => MENU_NORMAL_ITEM, ); $items['admin/people/subscriptions'] = array( 'title' => "Subscriptions", 'access arguments' => array('manage all subscriptions'), 'page callback' => 'drupal_get_form', 'page arguments' => array('simple_subscription_admin_form'), 'file' => 'simple_subscription.pages.inc', 'type' => MENU_LOCAL_TASK, ); $items['admin/people/subscriptions/list'] = array( 'title' => "List", 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 0, ); $items['admin/people/subscriptions/export'] = array( 'title' => "Export", 'access arguments' => array('manage all subscriptions'), 'page callback' => 'drupal_get_form', 'page arguments' => array('simple_subscription_export_form'), 'file' => 'simple_subscription.pages.inc', 'type' => MENU_LOCAL_TASK, 'weight' => 1, ); $items['newsletter/unsubscribe/%simple_subscription'] = array( 'title' => 'Unsubscribe from newsletter', 'access arguments' => array('manage own subscription'), 'page callback' => 'simple_subscription_unsubscribe_page', 'page arguments' => array(2), 'file' => 'simple_subscription.pages.inc', 'type' => MENU_CALLBACK, ); $items['newsletter/unsubscribe-hash/%simple_subscription_hash'] = array( 'title' => 'Unsubscribe from newsletter', 'access arguments' => array('manage own subscription'), 'page callback' => 'simple_subscription_unsubscribe_page', 'page arguments' => array(2), 'file' => 'simple_subscription.pages.inc', 'type' => MENU_CALLBACK, ); return $items; } /** * Load multiple subscriptions based on certain conditions. * * @param array $sids * An array of subscription SIDs * @param array $conditions * An array of the loading conditions (deprecated ? cf. user_load_multiple() ) * @param bool $reset * Reset the internal cache ? * @return * An array of subscription objects, indexed by sid. * * @see user_load_multiple() */ function simple_subscription_load_multiple($sids = array(), $conditions = array(), $reset = FALSE) { return entity_load('subscription', $sids, $conditions, $reset); } /** * Loads a subscription object. * * @param int $sid * The SID to load. * @param bool $reset * Reset internal cache ? * @return object|boolean * A subscription object, or FALSE. */ function simple_subscription_load($sid, $reset = FALSE) { $subscriptions = simple_subscription_load_multiple( array($sid), array(), $reset ); return reset($subscriptions); } /** * Loads a subscription object by hash. * * @param $hash string The subscrition hash to load * The md5 e-mail to load. * @return object|boolean * A subscription object, or FALSE. */ function simple_subscription_md5_load($hash) { $subscriptions = simple_subscription_load_multiple( array(), array('hash' => $hash) ); return reset($subscriptions); } /** * Fetch a subscription object by mail. * * @param string $mail * String with the subscription's mail. * @return object|boolean * A subscription object, or FALSE. */ function simple_subscription_load_by_mail($mail) { $subscriptions = simple_subscription_load_multiple( array(), array('mail' => $mail) ); return reset($subscriptions); } /** * Fetch a subscription object by UID. * * @param $uid * String with the subscription's uid. * @return object|boolean * A subscription object, or FALSE. */ function simple_subscription_load_by_uid($uid) { $subscriptions = simple_subscription_load_multiple( array(), array('uid' => $uid) ); return reset($subscriptions); } /** * Saves a subscription object or array. * * If $subscription->sid is set performs un update on the existing * account, else : creates a new account. * * @param $subscription * Array or object containing at least a mail to subscribe. * @return bool|object * The saved subscription object, or FALSE. */ function simple_subscription_save(&$subscription) { global $user; if (!is_object($subscription)) { $subscription = (object) $subscription; } if (!isset($subscription->mail)) { return FALSE; } if (!isset($subscription->created)) { $subscription->created = time(); } if (!isset($subscription->status)) { $subscription->status = 1; } if (!isset($subscription->hash)) { $subscription->hash = simple_subscription_generate_hash(); } if (variable_get('ssubscription_user_account_attach', TRUE)) { if (!isset($subscription->uid)) { $subscription->uid = $user->uid; } } else { //Impose uid=0 if subscriptions are not attached to user accounts $subscription->uid = 0; } $op = isset($subscription->sid) ? 'update' : 'insert'; $success = drupal_write_record('simple_subscription_subscriptions', $subscription, $op == 'update' ? 'sid' : array()); if ($success) { $msg = $op == 'insert' ? 'New subscription added (@submitted_email).' : 'Subscription updated (@submitted_email)'; watchdog('simple_subscription', $msg, array('@submitted_email' => $subscription->mail)); } else { $msg = 'An error occurred while adding @submitted_email'; watchdog('simple_subscription', $msg, array('@submitted_email' => $subscription->mail)); } module_invoke_all('subscription_' . $op, $subscription); module_invoke_all('entity_' . $op, $subscription, 'subscription'); return $success ? $subscription : FALSE; } /** * Deletes a subscription record for the database. * * @param $sid * The subscription's SID */ function simple_subscription_delete($sid) { simple_subscription_delete_multiple(array($sid)); } /** * Deletes multiple subscription records. * * @param array $sids * An array with the SIDs to delete */ function simple_subscription_delete_multiple(array $sids) { $subscriptions = simple_subscription_load_multiple($sids); foreach ($subscriptions as $subscription) { module_invoke_all('subscription_delete', $subscription); module_invoke_all('entity_delete', $subscription, 'subscription'); } db_delete('simple_subscription_subscriptions') ->condition('sid', $sids, 'IN') ->execute(); entity_get_controller('subscription')->resetCache(); } /** * Sets a subscription's status to 0 * * @param $sid * The subscription's SID * @return void */ function simple_subscription_unsubscribe($sid) { simple_subscription_unsubscribe_multiple(array($sid)); } /** * Sets multiple subcriptions status' to 0 * * @param array $sids * An array with the SIDs to alter * @return void */ function simple_subscription_unsubscribe_multiple(array $sids) { $subscriptions = simple_subscription_load_multiple($sids); foreach ($subscriptions as $subscription) { $subscription->status = 0; $subscription->deleted = time(); simple_subscription_save($subscription); } } /** * Implementation of hook_user_load(). */ function simple_subscription_user_load($accounts) { if (!variable_get('ssubscription_user_account_attach', TRUE)) { return; } foreach($accounts as $uid => &$account) { if (!$uid) { continue; } if ($subscription = simple_subscription_load_by_uid($uid)) { $account->simple_subscription = $subscription; } } } /** * Implementation of hook_user_insert(). */ function simple_subscription_user_insert(&$edit, $account, $category) { if (variable_get('ssubscription_user_account_attach', TRUE) && isset($account->simple_subscription)) { simple_subscription_save($account->simple_subscription); } } /** * Implementation of hook_user_update(). */ function simple_subscription_user_update(&$edit, $account, $category) { if (variable_get('ssubscription_user_account_attach', TRUE) && isset($account->simple_subscription)) { simple_subscription_save($account->simple_subscription); } } /** * Implementation of hook_user_delete(). */ function simple_subscription_user_delete($account) { if (!isset($account->simple_subscription)) { return; } //Act on the subscription record according to the configuration options. switch (variable_get('ssubscription_delete_user_action', SSUBSCRIPTION_DELETE_USER_NOTHING)) { case SSUBSCRIPTION_DELETE_USER_UNSUBSCRIBE: simple_subscription_unsubscribe($account->simple_subscription->sid); break; case SSUBSCRIPTION_DELETE_USER_DELETE: simple_subscription_delete($account->simple_subscription->sid); break; } } /** * Implements hook_block_info(). */ function simple_subscription_block_info() { $blocks['subscribe'] = array( 'info' => t('Subscription Block'), ); return $blocks; } /** * Implements hook_block_configure(). */ function simple_subscription_block_configure($delta = '') { $form = array(); if ($delta == 'subscribe') { $form = simple_subscription_block_config_form(); } return $form; } /** * Implements hook_block_save(). */ function simple_subscription_block_save($delta = '', $edit = array()) { if ($delta == 'subscribe') { $default_config = simple_subscription_get_default_values(); variable_set('simple_subscription_config', array( 'form_header' => $edit['simple_subscription_form_header'], 'form_footer' => $edit['simple_subscription_form_footer'], 'input_label' => $edit['simple_subscription_input_label'], 'input_size' => $edit['simple_subscription_input_size'], 'input_content' => $edit['simple_subscription_input_content'], 'submit_value' => empty($edit['simple_subscription_submit_value']) ? $default_config['submit_value'] : $edit['simple_subscription_submit_value'], 'success_message' => $edit['simple_subscription_success_message'], 'redirect_path' => $edit['simple_subscription_redirect_path'], )); } } /** * Implements hook_block_view(). */ function simple_subscription_block_view() { $return = array( 'subject' => t('Subscription'), 'content' => drupal_get_form('simple_subscription_form'), ); return $return; } /** * Implements hook_trigger_info(). */ function simple_subscription_trigger_info() { return array( 'user' => array( 'simple_subscription_submit' => array( 'label' => t('A user subscribed an e-mail address using the simple_subscription block'), ), ), ); } /** * Implements hook_token_info(). */ function simple_subscription_token_info() { $tokens = array( 'types' => array( 'simple-subscription' => array( 'name' => 'Simple subscription', 'description' => t('Simple subscription form data'), 'needs-data' => 'simple-subscription', ), ), 'tokens' => array( 'simple-subscription' => array( 'sid' => array( 'name' => t('Subscription id'), 'description' => t('The subscription ID.'), ), 'email' => array( 'name' => t('Submitted email address'), 'description' => t('Email address submitted using the Simple subscription block.'), ), 'unsubscribe-link' => array( 'name' => t('Unsubscribe link'), 'description' => t('Link to unsubscribe with the subscription hashed.'), ), ), ), ); return $tokens; } /** * Implements hook_tokens(). */ function simple_subscription_tokens($type, $tokens, array $data = array(), array $options = array()) { $return = array(); if (($type != 'simple-subscription') || (!isset($data['simple-subscription']))) { return $return; } foreach ($tokens as $machine_name => $raw_text) { switch ($machine_name) { case 'sid': if (isset($data['simple-subscription']['sid'])) { $return[$raw_text] = $data['simple-subscription']['sid']; } break; case 'email': if (isset($data['simple-subscription']['mail'])) { $return[$raw_text] = $data['simple-subscription']['mail']; } break; case 'unsubscribe-link': if (isset($data['simple-subscription']['hash'])) { $url = 'newsletter/unsubscribe-hash/' . $data['simple-subscription']['hash']; $return[$raw_text] = l(t('Unsubscribe'), $url, array( 'absolute' => TRUE, )); } break; default: break; } } return $return; } /** * Returns the config form for our block * * If Token module is available, adds the standard fieldset with the available tokens * * @return array The form array */ function simple_subscription_block_config_form() { $default_config = simple_subscription_get_default_values(); $config = variable_get('simple_subscription_config', $default_config); $form = array(); $form['simple_subscription_form_header'] = array( '#type' => 'textarea', '#title' => t('Form header'), '#default_value' => filter_xss_admin($config['form_header']), '#description' => t('This text will be displayed before the form elements (leave empty for none). You may use Html tags.'), ); $form['simple_subscription_form_footer'] = array( '#type' => 'textarea', '#title' => t('Form footer'), '#default_value' => filter_xss_admin($config['form_footer']), '#description' => t('This text will be displayed after the form elements (leave empty for none). You may use Html tags.'), ); $form['token_help'] = array( '#title' => t('Replacement patterns for the text fields above'), '#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => TRUE, ); if (module_exists('token')) { $form['token_help']['token_tree'] = array( '#theme' => 'token_tree', '#token_types' => token_get_global_token_types(), ); } else { $form['token_help']['install_token'] = array( '#markup' => t('

Install the Token module to see a list of the available tokens

'), ); } $form['simple_subscription_input_label'] = array( '#type' => 'textfield', '#title' => t('Input field label'), '#size' => 20, '#default_value' => check_plain($config['input_label']), '#description' => t('A label for the e-mail input field (Leave empty for none).'), ); $form['simple_subscription_input_size'] = array( '#type' => 'select', '#title' => t('Input field size'), '#default_value' => $config['input_size'], '#description' => t('Html size attribute for the e-mail input field.'), '#options' => array( 15 => '15', 20 => '20', 25 => '25', 30 => '30', 35 => '35', 40 => '40', 45 => '45', 50 => '50' ), ); $form['simple_subscription_input_content'] = array( '#type' => 'textfield', '#title' => t('Input field text'), '#size' => 20, '#default_value' => check_plain($config['input_content']), '#description' => t('A short string to display inside the input field. This text is added by javascript and will only be visible if the field is empty and not focused (only on browsers with javascript activated). Be careful not to repeat the information you added in the field label above.'), ); $form['simple_subscription_submit_value'] = array( '#type' => 'textfield', '#title' => t('Submit button value'), '#size' => 20, '#default_value' => check_plain($config['submit_value']), '#description' => t('Contents for the form submit button. Cannot be empty and defaults to @default_value. No tokens are allowed.', array('@default_value' => $default_config['submit_value'])), ); $form['simple_subscription_success_message'] = array( '#type' => 'textfield', '#title' => t('Success message'), '#size' => 60, '#default_value' => filter_xss_admin($config['success_message']), '#description' => t('The message to display on successful e-mail submission (leave empty for none). You may use Html tags and you may use the aditionnal [simple-subscription:email] token.'), ); $form['simple_subscription_redirect_path'] = array( '#type' => 'textfield', '#title' => t('Redirect path'), '#size' => 60, '#default_value' => check_plain($config['redirect_path']), '#description' => t("The internal path to redirect the user to after a successful login. Use '/' for the front page, leave empty to stay on the same page."), '#element_validate' => array('simple_subscription_redirect_path_validate'), ); return $form; } /** * Validation function for the redirect path field in the block configuration form. * * We don't accept external URLs (seems logical in this context, but I might be wrong). * * @param $element * @param $form_state */ function simple_subscription_redirect_path_validate($element, &$form_state) { if (!$element['#value']) { return; } else { $normal_path = drupal_get_normal_path($element['#value']); if (url_is_external($normal_path)) { form_set_error($element['#name'], t("Invalid redirect path @redirect_path. Only internal redirections can be added.", array( '@redirect_path' => $element['#value'] )) ); } } } /** * The simple_subscription form. * * As will all forms in D7, you can theme this one by declaring a * 'simple_subscription_form' theme function or template in your * theme layer. * * @return array The form array. */ function simple_subscription_form() { global $user; $account = user_load($user->uid); $default_config = simple_subscription_get_default_values(); $config = variable_get('simple_subscription_config', $default_config); $form = array(); if (!empty($config['form_header'])) { $form['header'] = array( '#prefix' => '
', '#markup' => token_replace(filter_xss_admin($config['form_header'])), '#suffix' => '
', ); } /* * E-mail input field. */ if (variable_get('ssubscription_user_account_attach', TRUE) && (variable_get('ssubscription_block_user_options', SSUBSCRIPTION_BLOCK_VISIBILITY_PREFILL) == SSUBSCRIPTION_BLOCK_VISIBILITY_PREFILL) && isset($account->simple_subscription) && $account->simple_subscription->status) { $default_mail = $account->simple_subscription->mail; } else { $default_mail = ''; } $form['mail'] = array( '#type' => 'textfield', '#default_value' => $default_mail, '#maxlength' => 255, '#size' => $config['input_size'], '#required' => TRUE, //@FIXME doesn't seem to work ! '#element_validate' => array('simple_subscription_email_validate', 'simple_subscription_verify_duplicate'), '#attributes' => array('class' => array('edit-mail')), ); if ($config['input_label']) { $form['mail']['#title'] = check_plain($config['input_label']); } /* * Add JS behavior if the input_content field is not empty */ $input_content = trim(check_plain($config['input_content'])); if (!empty($input_content)) { $form['mail']['#attached']['js'][] = drupal_get_path('module', 'simple_subscription') . '/simple_subscription.js'; $form['mail']['#attached']['js'][] = array( 'data' => array( 'simple_subscription' => array('input_content' => ($input_content)) ), 'type' => 'setting', ); $form['mail']['#attached']['css'][] = drupal_get_path('module', 'simple_subscription') . '/simple_subscription.css'; } if (!empty($config['form_footer'])) { $form['footer'] = array( '#prefix' => '', ); } $form['submit'] = array( '#type' => 'submit', '#value' => token_replace(trim(check_plain($config['submit_value']))), // @FIXME doesn't render quotes properly ); return $form; } /** * Validation function for the simple_subscription form. * * We validate e-mail address using the default Drupal function valid_email_address(). * * @param string $form * @param string $form_state * @return void */ function simple_subscription_email_validate($element, &$form_state, $form) { if (empty($element['#value']) || !valid_email_address($element['#value'])) { form_set_error($element['#name'], t("Invalid e-mail address '@submitted_email'. Please verify your syntax.", array('@submitted_email' => check_plain($element['#value']))) ); } } /** * Verify that there is no duplicate subscription. * * @param $element * @param $form_state * @param $form */ function simple_subscription_verify_duplicate($element) { global $user; //load full $user object $account = user_load($user->uid); if (!($subscription = simple_subscription_load_by_mail($element['#value']))) { return; } $user_subscribed = isset($account->simple_subscription); if (!$user_subscribed || $subscription->uid != $account->uid) { form_set_error($element['#name'], t("The e-mail '@submitted_email' is already registered.", array('@submitted_email' => check_plain($element['#value']))) ); } } /** * Submit function for the simple_subscription form. * * We invoke all the modules hooked on hook_simple_subscription, trigger all the actions * associated with the form submition and display the configured success message. * * @param $form * @param $form_state */ function simple_subscription_form_submit($form, &$form_state) { $subscription = simple_subscription_process_subscription($form_state['values']['mail']); $default_config = simple_subscription_get_default_values(); $config = variable_get('simple_subscription_config', $default_config); $data = array( 'simple-subscription' => array( 'mail' => $subscription->mail, 'sid' => $subscription->sid, 'hash' => $subscription->hash, ), ); //sanitize the message $msg = filter_xss_admin(trim($config['success_message'])); $msg = token_replace($msg, $data); if (!empty($msg)) { drupal_set_message($msg); } $aids = trigger_get_assigned_actions('simple_subscription_submit'); // prepare a basic context, indicating group and "hook", and call all the // actions with this context as arguments. $context = array( 'group' => 'user', 'hook' => 'simple_subscription_submit', ); $context += $data; actions_do(array_keys($aids), NULL, $context); $path = trim($config['redirect_path']); if ($path) { $form_state['redirect'] = drupal_get_normal_path($path); } } /** * Process the subscription of a user (create or update). * * @param $mail * @return mixed|\stdClass */ function simple_subscription_process_subscription($mail) { global $user; $account = user_load($user->uid); //load full $user object $save = FALSE; if (isset($account->simple_subscription)) { if ($account->simple_subscription->mail == $mail) { if ($account->simple_subscription->status) { $msg = t('You are already subscribed to our newsletter.'); $subscription = $account->simple_subscription; } else { $subscription = $account->simple_subscription; $subscription->status = 1; $subscription->created = time(); $msg = t('Welcome back to our newsletter.'); $save = TRUE; } } else { $account->simple_subscription->mail = $mail; $msg = t('Your subscription has been updated.'); $subscription = $account->simple_subscription; $save = TRUE; } } elseif ($subscription = simple_subscription_load_by_mail($mail)) { if ($subscription->status) { $msg = t('You are already subscribed to our newsletter.'); } else { $msg = t('Welcome back to our newsletter.'); $subscription->status = 1; $subscription->created = time(); $save = TRUE; } } else { $msg = t("You were successfully added to the newsletter recipients."); $subscription = new stdClass(); $subscription->mail = $mail; $subscription->status = 1; $subscription->created = time(); $save = TRUE; } if ($save && !simple_subscription_save($subscription)) { drupal_set_message(t('There was a problem writing to the database. Contact the site administrator.')); } else { drupal_set_message($msg); } return $subscription; } /** * Returns the simple_subscription default values. * * @return array An array with the block's default values */ function simple_subscription_get_default_values() { return array( 'form_header' => t('Enter your e-mail address below.'), 'form_footer' => t('Opt-out instructions are included in each e-mail.'), 'input_label' => '', 'input_size' => 20, 'input_content' => t('e-mail address'), 'submit_value' => t('Subscribe'), 'success_message' => t('Thank you for subscribing'), 'redirect_path' => '', ); } /** * Utility function to generate the subscriptions security hash. * * @return string */ function simple_subscription_generate_hash() { return drupal_hash_base64(drupal_random_bytes(55)); } /** * Implements hook_views_api(). */ function simple_subscription_views_api() { return array( 'api' => 3, 'path' => drupal_get_path('module', 'simple_subscription'), ); }