'Reminder page', 'description' => 'Access a reminder page', 'page callback' => 'reminder_page', 'page arguments' => array(1), 'access arguments' => array('show reminder'), 'file' => 'reminder.pages.inc', 'type' => MENU_CALLBACK, ); $items['reminder/%/unsubscribe'] = array( 'title' => 'Unsubscribe page', 'description' => 'Unsubscribe from a reminder', 'page callback' => 'reminder_unsubscribe', 'page arguments' => array(1), 'access arguments' => array('show reminder'), 'file' => 'reminder.pages.inc', 'type' => MENU_CALLBACK, ); $items['reminder/%/log'] = array( 'title' => 'Log page', 'description' => 'Show logs', 'page callback' => 'reminder_logpage', 'page arguments' => array(1), 'access arguments' => array('show reminder'), 'file' => 'reminder.pages.inc', 'type' => MENU_CALLBACK, ); $items['reminder/%/denounce'] = array( 'title' => 'Log page', 'description' => 'Show logs', 'page callback' => 'reminder_denounce', 'page arguments' => array(1), 'access arguments' => array('show reminder'), 'file' => 'reminder.pages.inc', 'type' => MENU_CALLBACK, ); $items['user/%user/reminder'] = array( 'title' => 'My reminders', 'description' => 'List of my reminders', 'page callback' => 'reminder_mypage', 'access callback' => '_reminder_access_userreminders', 'access arguments' => array(1), 'file' => 'reminder.pages.inc', 'type' => MENU_SUGGESTED_ITEM, ); return $items; } /** * Implements hook_perm() */ function reminder_permission() { return array( 'access all reminders' => array( 'title' => t("Access all reminders"), 'restrict access' => TRUE, ), 'access own reminders' => array( 'title' => t("Access own reminders"), ), 'show reminder' => array( 'title' => t("Access reminders"), ), ); } /** * Implements hook_node_info() */ function reminder_node_info() { return array( 'reminder' => array( 'name' => t('Reminder'), 'base' => 'reminder', 'description' => t("Create a reminder for a business lunch, a meeting or a movie night."), 'has_title' => TRUE, 'title_label' => t('Your reminder'), ) ); } /** * Implementation of hook_form_alter() */ function reminder_form_alter(&$form, $form_state, $form_id) { // Remove preview button from reminder node form if ($form_id == 'reminder_node_form') { unset($form['actions']['preview']); } } /** * Implementation of hook_node_access() */ function reminder_node_access($node, $op, $account) { if (is_object($node) && $node->type == 'reminder') { switch ($op) { // disabling the edit page: we don't give access to any user // reason: we're using custom urls to update the nodes, and only // the owner of the reminder has the custom admin url case 'update': return FALSE; case 'delete': return user_access('delete reminder', $account) && ($account->uid == $node->uid); } } } /** * Implementation of hook_node_form() */ function reminder_form(&$node) { global $user; $type = node_type_get_type($node); $format_until = 'Y-m-d H:i'; $reminder = isset($node->nid) ? reminder_get_reminder($node->nid, TRUE) : array(); if ($type->has_title) { $form['title'] = array( '#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5 ); } $form['description'] = array( '#type' => 'textarea', '#title' => t('Add some description'), '#default_value' => isset($reminder['description']) ? $reminder['description']: '', '#rows' => 5 ); if ((isset($node->nid) && $node->uid == 0) || $user->uid == 0) { $form['anonym'] = array( '#type' => 'fieldset', '#title' => t('Add your name and email'), '#tree' => TRUE, ); $form['anonym']['user_name'] = array( '#type' => 'textfield', '#title' => t('Your name'), '#maxlength' => 100, '#description' => t('This is optional.'), '#default_value' => isset($reminder['anonym_name']) ? $reminder['anonym_name'] : '', ); $form['anonym']['user_email'] = array( '#type' => 'textfield', '#title' => t('Your e-mail'), '#description' => t(''), '#maxlength' => 100, '#default_value' => isset($reminder['anonym_email']) ? $reminder['anonym_email'] : '', ); } if (isset($node->nid)) { $reminder['subscriptions'] = reminder_get_subscriptions($node->nid); } $form['until'] = array( '#type' => 'date_popup', '#title' => 'Remind until', '#default_value' => isset($reminder['until']) ? $reminder['until'] : date($format_until), '#date_format' => $format_until, '#date_label_position' => 'within', '#date_increment' => 60, '#date_year_range' => '0:+20', ); $form['frequency'] = array( '#type' => 'select', '#title' => t('Interval'), '#options' => array( 'hourly' => t('Hourly'), 'daily' => t('Daily'), 'weekly' => t('Weekly'), 'monthly' => t('Monthly'), 'yearly' => t('Yearly'), 'decreasing' => t('Increasing (you get more reminders over time)'), ), '#default_value' => isset($reminder['frequency']) ? $reminder['frequency'] : 'hourly', '#description' => t('Choose the frequency a reminder should be sent to you and all subscribers.'), ); $form['reminder_options'] = array( '#type' => 'fieldset', '#title' => t('Reminder options'), '#tree' => TRUE, ); $form['reminder_options']['secure'] = array( '#type' => 'radios', '#title' => t('Show subscribed emails'), '#description' => t("Deny subscribers to see each other."), '#options' => array('0' => t('No'), '1' => t('Yes')), '#default_value' => isset($reminder['secure']) ? $reminder['secure'] : 0, ); $form['reminder_options']['now'] = array( '#type' => 'radios', '#title' => t('Issue a reminder right now to all subscribers'), '#description' => t("Deny subscribers to see each other."), '#options' => array('0' => t('No'), '1' => t('Yes')), '#default_value' => 0, ); $form['reminder_subscriptions'] = array( '#type' => 'textarea', '#title' => t('Subscribers'), '#description' => t('One valid email address per line.'), '#maxlength' => 100, '#default_value' => isset($reminder['subscriptions']) ? implode($reminder['subscriptions'], '\n'): NULL, ); return $form; } /** * Form validation. * * @todo * Throttling settings to avoid SPAM: max posts per * hour and max recipients per post. * * Validate name and email if not registered user. * Date and interval validation. * Test all fields. */ function reminder_validate($node, $form, &$form_state) { if ($form['type']['#value'] == 'reminder') { // TODO // valid_email_address() } } /** * Implementation of hook_node_submit() * * @todo * Date, remind and subscribe widgets. * Update subscribers, removing old ones. * Optionally send unsubscription email for removed users. * Redirect to reminder page. */ function reminder_node_submit($node, $form, &$form_state) { global $user; // No preview available. if (!$form_state['submitted']) { return; } if (isset($form_state['values']['anonym'])) { $anonym_name = $form_state['values']['anonym']['user_name']; $anonym_email = $form_state['values']['anonym']['user_email']; } else { $anonym_name = NULL; $anonym_email = NULL; } $node->title = $form_state['values']['title']; if ($node->nid == NULL) { $action = 'create'; // Save basic data. node_save($node); // Generate the reminder urls and save them. $reminder_url = _reminder_generate_url('url', 10); $admin_url = _reminder_generate_url('admin_url', 25); // Save reminder options. $values = array( 'nid' => $node->nid, 'uid' => $user->uid, 'until' => $form_state['values']['until'], 'frequency' => $form_state['values']['frequency'], 'secure' => $form_state['values']['reminder_options']['secure'], 'url' => $reminder_url, 'admin_url' => $admin_url, 'anonym_name' => $anonym_name, 'anonym_email' => $anonym_email, 'description' => $form_state['values']['description'], ); $query = db_insert('reminder')->fields($values); $query->execute(); // Save subscribers. foreach (reminder_parse_subscribers($node->reminder_subscriptions) as $subscriber) { $unsubscribe_url = _reminder_generate_url('unsubscribe_url', 30, 'reminder_subscriptions'); $values = array( 'email' => $subscriber, 'reminder_id' => $node->nid, 'unsubscribe_url' => $unsubscribe_url, ); $query = db_insert('reminder_subscriptions')->fields($values)->execute(); } } else { // Fetch existing options. $reminder = reminder_get_reminder($node->nid); $reminder_url = $reminder['url']; $admin_url = $reminder['admin_url']; $action = 'update'; // Update basic data. node_save($node); // Save reminder options db_update('reminder')->fields(array( 'until' => $form_state['values']['until'], 'frequency' => $form_state['values']['frequency'], 'secure' => $form_state['values']['reminder_options']['secure'], 'anonym_name' => $anonym_name, 'anonym_email' => $anonym_email, ))->condition('nid', $node->nid)->execute(); // Update subscribers, removing old ones. $current_subscriptions = array(); $current_subscriptions_data = reminder_get_subscriptions($node->nid); $new_subscriptions = reminder_parse_subscribers($node->reminder_subscriptions); foreach ($current_subscriptions_data as $subscriber) { // We copy just the relevant data just for later use. $current_subscriptions[$subscriber['id']] = $subscriber['email']; // Unsubscribe user from reminder. if (!in_array($subscriber['email'], $new_subscriptions)) { db_delete('reminder_subscriptions') ->condition('id', $subscriber['id']) ->execute(); } } foreach ($new_subscriptions as $subscriber) { if (!in_array($subscriber, $current_subscriptions)) { $unsubscribe_url = _reminder_generate_url('unsubscribe_url', 30, 'reminder_subscriptions'); $values = array( 'email' => $subscriber, 'reminder_id' => $node->nid, 'unsubscribe_url' => $unsubscribe_url, ); } } } $mail = ($user->uid > 0) ? $mail = $user->mail : $form_state['values']['anonym']['user_email']; $name = ($user->uid > 0) ? $user->name : $form_state['values']['anonym']['user_name']; // Send message to reminder owner drupal_mail('reminder', $action .'_reminder', $mail, language_default(), array( "name" => $name, "reminder_url" => $reminder_url, "admin_url" => $admin_url, ) ); // Send reminder now. if (isset($form_state['reminder_options']['now']) && $form_state['reminder_options']['now'] != 0) { reminder_send($node->nid); } drupal_set_message( l( t("Reminder page URL: !url", array("!url" => url('reminder/' . $reminder_url, array("absolute" => TRUE)))), "reminder/" . $reminder_url ) ); drupal_set_message( l( t("Admin page URL: !url", array("!url" => url('reminder/' . $admin_url, array("absolute" => TRUE)))), "reminder/" . $admin_url ) ); drupal_set_message(t("Saved.")); } /** * Implements hook_mail() * * @todo */ function reminder_mail($key, &$message, $params) { $subject = t('Reminder information'); $body = t('Reminder URL: @url', array('@url' => $params['reminder_url'])); $message['subject'] .= str_replace(array("\r", "\n"), '', $subject); $message['body'][] = drupal_html_to_text($body); if ($key == 'create_reminder') { } else if ($key == 'update_reminder') { } } /** * Implementation of hook_node_delete() */ function reminder_delete($node) { db_query("DELETE FROM {reminder} WHERE nid = :nid", array(':nid' => $node->nid)); db_query("DELETE FROM {reminder_logs} WHERE nid = :nid", array(':nid' => $node->nid)); db_query("DELETE FROM {reminder_subscriptions} WHERE reminder_id = :reminder_id", array(':reminder_id' => $node->nid)); db_query("DELETE FROM {reminder_notifications} WHERE reminder_id = :reminder_id", array(':reminder_id' => $node->nid)); } /** * Implements hook_cron() * * @todo * Send periodic emails. */ function reminder_cron() { variable_set('reminder_cron_last_run', REQUEST_TIME); } /** * Send emails for all subscribers. * * @todo */ function reminder_send($nid) { } /** * Helper function to parse subscribers. * * @see http://stackoverflow.com/questions/1028553/how-to-get-email-address-from-a-long-string */ function reminder_parse_subscribers($subscribers) { foreach(preg_split('/ |,|;|\n/', $subscribers) as $token) { $email = filter_var(filter_var($token, FILTER_SANITIZE_EMAIL), FILTER_VALIDATE_EMAIL); if ($email !== false) { $emails[] = $email; } } return $emails; } /** * Helper function to generate a unique keychain * * @param $field * The field to be unique * @param $length * Lengh of the keychain * @return string */ function _reminder_generate_url($field, $length, $table = 'reminder') { $url = _reminder_keygen($length); $query = db_select($table, 'm')->fields('m'); while ($query->condition($field, $url)->execute()->rowCount() > 0) { $url = _reminder_keygen($length); } return $url; } /** * Helper function to generate a keychain * * @param $length * Lengh of the keychain * @return string */ function _reminder_keygen($length) { $pattern = "1234567890abcdefghijklmnopqrstuvwxyz"; $key = $pattern{rand(0, 35)}; for ($i = 1; $i < $length; $i++) { $key .= $pattern{rand(0, 35)}; } return $key; } /** * Menu access callback. */ function _reminder_access_userreminders($account) { global $user; return user_access('access all reminders') || ($user->uid == $account->uid && user_access('access own reminders')); } /** * Get reminder data. * * @param $nid * Reminder nid. * * @param $subscriptions * Whether to fetch subscription information. * * @return * Reminder result set. */ function reminder_get_reminder($nid, $subscriptions = FALSE) { // Schema include_once 'reminder.install'; $schema = reminder_schema(); $fields = array_keys($schema['reminder']['fields']); // Fetch existing options. $query = db_select('reminder', 'r'); $query ->condition('r.nid', $nid) ->fields('r', $fields); $results = $query->execute()->fetchAll(); // Sanitize the data before handing it off to the theme layer. foreach ($results as $entry) { // Return the first result as we just have one entry. $result = array_map('check_plain', (array) $entry); break; } if (!$subscriptions) { return $result; } $result['subscriptions'] = reminder_get_subscriptions($nid); return $result; } /** * Get subscription data. * * @param $nid * Reminder nid. * * @return * Subscriptions result set. */ function reminder_get_subscriptions($nid) { // Schema include_once 'reminder.install'; $schema = reminder_schema(); $fields = array_keys($schema['reminder_subscriptions']['fields']); // Fetch existing options. $query = db_select('reminder_subscriptions', 'r'); $query ->condition('r.reminder_id', $nid) ->fields('r', $fields); $results = $query->execute()->fetchAll(); // Sanitize the data before handing it off to the theme layer. foreach ($results as $entry) { $results[] = array_map('check_plain', (array) $entry); } return $results; }