'{signup} s', 'fields' => array('n.title', 'n.nid', 's.reminder_email', 's.forwarding_email'), 'where' => array('s.send_reminder = 1'), 'joins' => array('INNER JOIN {node} n ON n.nid = s.nid'), ); $sql = _signup_build_query($reminder_common_sql, $reminder_sql); $result = db_query($sql); // Grab each event, construct the email header and subject, and query // the signup log to pull all users who are signed up for this event. $from = variable_get('site_mail', 'noadmin@noadmin.com'); while ($event = db_fetch_object($result)) { $subject = t('Event reminder: !event', array('!event' => $event->title)); $signups = db_query("SELECT u.name, u.mail, s_l.anon_mail, s_l.form_data FROM {signup_log} s_l INNER JOIN {users} u ON u.uid = s_l.uid WHERE s_l.nid = %d", $event->nid); // Loop through the users, composing their customized message // and sending the email. while ($signup = db_fetch_object($signups)) { $mail_address = $signup->anon_mail ? $signup->anon_mail : $signup->mail; $signup_data_array = unserialize($signup->form_data); if (!empty($signup_data_array)) { $signup_data = signup_build_signup_data($signup_data_array, 'email'); $signup_info = t('SIGNUP INFORMATION') ."\n\r". $signup_data; } $trans = array( '%event' => $event->title, '%time' => signup_format_date($event), '%username' => $signup->name, '%useremail' => $mail_address, '%info' => $signup_info, ); $message = strtr($event->reminder_email, $trans); drupal_mail('signup_reminder_mail', $mail_address, $subject, $message, $from); watchdog('signup', t('Reminder for %event sent to %useremail.', array('%event' => $event->title, '%useremail' => $mail_address)), WATCHDOG_NOTICE, l(t('view'), 'node/'. $event->nid)); } // Reminders for this event are all sent, so mark it in the // database so they're not sent again. db_query("UPDATE {signup} SET send_reminder = 0 WHERE nid = %d", $event->nid); } } /** * Helper function that handles auto-closing events during cron. * * Invokes the method for the installed event/date backend module to get the * right query fragments, and builds a query to find all events where signups * should be closed (e.g. events that already started, etc). * * @see signup_cron() * @see signup_autoclose_sql() * @see _signup_build_query() */ function _signup_cron_autoclose() { if (function_exists('signup_autoclose_sql')) { $autoclose_sql = signup_autoclose_sql(); } if (empty($autoclose_sql)) { // The backend doesn't support auto-closing events, so bail out now. return; } $autoclose_common_sql = array( 'primary' => '{signup} s', 'fields' => array('s.nid'), 'where' => array('s.status = 1'), ); $sql = _signup_build_query($autoclose_common_sql, $autoclose_sql); $result = db_query($sql); // Loop through the results, calling the event closing function. while ($signup = db_fetch_object($result)) { signup_close_signup($signup->nid, $cron = 'yes'); $node = node_load($signup->nid); foreach (module_implements('signup_close') as $module) { $function = $module .'_signup_close'; $function($node); } watchdog('signup', t('Signups closed for %event by cron.', array('%event' => $node->title)), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid)); } } /** * Private query builder helper function. * * @param $common_sql * Nested array of shared query fragments that are common to all date-based * backends. The supported keys are: * 'primary': the query's primary table and its alias (required). * 'fields': array of fields to SELECT. * 'joins': array of JOIN statements for other tables. * 'where': array of WHERE clauses. * 'group_by': array of GROUP BY fields. * * @param $backend_sql * Similar nested array provided by the date-based backend include file, * except that 'primary' is not allowed. * * @return * Complete SQL statement based on the given query fragments. */ function _signup_build_query($common_sql, $backend_sql) { $fields = array_merge( (!empty($common_sql['fields']) ? $common_sql['fields'] : array()), (!empty($backend_sql['fields']) ? $backend_sql['fields'] : array()) ); $joins = array_merge( (!empty($common_sql['joins']) ? $common_sql['joins'] : array()), (!empty($backend_sql['joins']) ? $backend_sql['joins'] : array()) ); $where = array_merge( (!empty($common_sql['where']) ? $common_sql['where'] : array()), (!empty($backend_sql['where']) ? $backend_sql['where'] : array()) ); $group_by = array_merge( (!empty($common_sql['group_by']) ? $common_sql['group_by'] : array()), (!empty($backend_sql['group_by']) ? $backend_sql['group_by'] : array()) ); $sql = 'SELECT '. implode(', ', $fields); $sql .= ' FROM '. $common_sql['primary'] .' '; if (!empty($joins)) { $sql .= implode(' ', $joins); } if (!empty($where)) { $sql .= ' WHERE '. implode(' AND ', $where); } if (!empty($group_by)) { $sql .= ' GROUP BY '. implode(', ', $group_by); } return $sql; } /** * Implementation of hook_help(). * @ingroup signup_core */ function signup_help($section) { switch ($section) { case 'admin/help#signup': return '

'. t('Signup allows users to sign up (in other words, register) for content of any type. The most common use is for events, where users indicate they are planning to attend. This module includes options for sending a notification email to a selected email address upon a new user signup (good for notifying event coordinators, etc.) and a confirmation email to users who sign up. Each of these options are controlled per node. When used on event nodes (with event.module installed and regular cron runs), it can also send out reminder emails to all signups a configurable number of days before the start of the event (also controlled per node) and to automatically close event signups 1 hour before their start (general setting). Settings exist for resticting signups to selected roles and content types.') .'

'. t('To use signup, you must enable which content types should allow signups in administer->settings->content types, and you must also grant the %sign_up_for_content permission to any user role that should be able to sign up in administer->access control. Each signup-enabled node will now have a place for users to sign up.', array('%sign_up_for_content' => 'sign up for content')) .'

'. t('There are two ways for a user to have administrative access to the signup information about a given node: either the user has the %administer_signups_for_own_content permission and they are viewing a node they created, or the user has the global %administer_all_signups permission. Administrative access allows a user to view all the users who have signed up for the node, along with whatever information they included when they signed up. Signup administrators can also cancel other user\'s signups for the node, and can close signups on the node entirely (meaning no one else is allowed to sign up).', array('%administer_signups_for_own_content' => 'administer signups for own content', '%administer all signups' => 'administer all signups')) .'

'. t('Default settings for notification email address, reminder emails and confirmation emails are located in administer->settings->signup. These will be the default values used for a signup node unless otherwise specified (to configure these options per node, visit \'edit\' for that node and make the adjustments in the \'Sign up settings\' section).') .'

'. t('Signups can be manually closed for any node at the %signup_overview page, or on the \'signups\' tab on each node.', array('%signup_overview' => t('Signup overview'))) .'

'. t('The user signup form is fully themable -- form fields may be added or deleted. For more details see the instructions in signup.theme, where a sample user form is included.') .'

'; } // If we're still here, consider the URL for help on various menu tabs. if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'signup-broadcast') { $node = node_load(arg(1)); return '

'. t('This page allows you to send an email message to every user who signed up for this @node_type.', array('@node_type' => $node->type)) .'

'; } } /** * Implmentation of hook_menu() * @ingroup signup_core */ function signup_menu($may_cache) { global $user; $items = array(); $access = user_access('administer all signups'); if ($may_cache) { $items[] = array( 'path' => 'admin/settings/signup', 'description' => t('Configure settings for signups.'), 'access' => $access, 'callback' => 'drupal_get_form', 'callback arguments' => array('signup_settings_page'), 'title' => user_access('access administration pages') ? t('Signup') : t('Signup settings'), ); $items[] = array( 'path' => 'admin/content/signup', 'description' => t('View all signup-enabled posts, and open or close signups on them.'), 'access' => $access, 'callback' => 'signup_admin_page', 'title' => t('Signup administration'), ); } else { // !$may_cache: dynamic menu items _signup_initialize_event_backend(); // Conditionally load either the views support, or the code that // only should happen if views is not enabled. $signup_path = './'. drupal_get_path('module', 'signup'); if (module_exists('views')) { require_once($signup_path .'/signup_views.inc'); } else { require_once($signup_path .'/signup_no_views.inc'); signup_no_views_menu($items, $may_cache); } // If it's a signup-enabled node, then put in a signup tab for admins. if (arg(0) == 'node' && is_numeric(arg(1))) { $node = node_load(array('nid' => arg(1))); if (!empty($node->signup)) { $access_own = user_access('administer signups for own content') && ($user->uid == $node->uid); $email_own = user_access('email users signed up for own content') && ($user->uid == $node->uid); $email_all = user_access('email all signed up users'); if (variable_get('signup_form_location', 'node') == 'tab' && _signup_needs_output($node)) { $items[] = array( 'path' => 'node/'. arg(1) .'/signup', 'title' => t('Sign up'), 'callback' => 'signup_node_tab', 'callback arguments' => array($node), 'type' => MENU_LOCAL_TASK, 'weight' => 19, ); } $items[] = array( 'path' => 'node/'. arg(1) .'/signups', 'title' => t('Signups'), 'callback' => 'signup_node_admin_page', 'callback arguments' => array($node), 'access' => $access || $access_own, 'type' => MENU_LOCAL_TASK, 'weight' => 20, ); $items[] = array( 'path' => 'node/'. arg(1) .'/signup-broadcast', 'title' => t('Signup broadcast'), 'callback' => 'drupal_get_form', 'callback arguments' => array('signup_broadcast_form', $node), 'access' => $email_all || $email_own, 'type' => MENU_LOCAL_TASK, 'weight' => 21, ); } } } return $items; } function _signup_initialize_event_backend() { define('SIGNUP_PATH', drupal_get_path('module', 'signup')); if (defined('EVENT_API') && EVENT_API == '5.2') { include_once(SIGNUP_PATH .'/signup_event_5.x-2.inc'); } else if (module_exists('event')) { include_once(SIGNUP_PATH .'/signup_event_5.x-1.inc'); } else if (module_exists('date')) { // include_once(SIGNUP_PATH .'/signup_date.inc'); // Until date.module support exists, fall back to the no event case. include_once(SIGNUP_PATH .'/signup_event_none.inc'); } else { include_once(SIGNUP_PATH .'/signup_event_none.inc'); } } /** * Implementation of hook_perm(). * @ingroup signup_core */ function signup_perm() { return array( 'sign up for content', 'cancel own signups', 'view all signups', 'administer all signups', 'administer signups for own content', 'email users signed up for own content', 'email all signed up users', ); } /** * Implementation of hook_form_alter(). * @ingroup signup_core */ function signup_form_alter($form_id, &$form) { switch ($form_id) { case 'node_type_form': signup_alter_node_type_form($form_id, $form); break; case $form['type']['#value'] .'_node_form': signup_alter_node_form($form_id, $form); break; } } /** * Alters the form for administrator settings per node type. * (admin/content/types) */ function signup_alter_node_type_form($form_id, &$form) { $type = $form['old_type']['#value']; $form['workflow']['signup_node_default_state'] = array( '#type' => 'radios', '#title' => t('Signup options'), '#options' => array( 'disabled' => t('Disabled'), 'allowed_off' => t('Allowed (off by default)'), 'enabled_on' => t('Enabled (on by default)'), ), '#default_value' => variable_get('signup_node_default_state_'. $type, 'disabled'), '#description' => t('If %disabled is selected, signups will not be possible for this content type. If %allowed_off is selected, signups will be off by default, but users with the %admin_all_signups permission will be able to allow signups for specific posts of this content type. If %enabled_on is selected, users will be allowed to signup for this content type unless an administrator disbles signups on specific posts.', array('%disabled' => t('Disabled'), '%allowed_off' => t('Allowed (off by default)'), '%enabled_on' => t('Enabled (on by default)'), '%admin_all_signups' => t('administer all signups'))), ); } /** * Alters the node form to inject the appropriate per-node signup settings. */ function signup_alter_node_form($form_id, &$form) { global $user; // Load the node if it already exists. if (!empty($form['nid']['#value'])) { $node = node_load($form['nid']['#value']); } else { $node = NULL; } $signup_type_default = variable_get('signup_node_default_state_'. $form['type']['#value'], 'disabled'); // If the current user has the global 'administer all signups' permission // and signups are not explicitly disallowed, or if this node-type is // signup-enabled and the user has permission to administer signups for // their own content, add a fieldset for signup-related settings. if ( (($signup_type_default != 'disabled' || (!empty($node) && !empty($node->signup))) && user_access('administer all signups')) || (!empty($node) && $signup_type_default == 'enabled_on' && $node->uid == $user->uid && user_access('administer signups for own content')) ) { $form['signup'] = array( '#type' => 'fieldset', '#title' => t('Signup settings'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => 30, ); // Figure out what the options should be. If there are already // people signed-up for this node, we need a 3rd choice: disable // signups and remove all signup data. $has_signups = !empty($node) && db_result(db_query("SELECT COUNT(*) from {signup_log} WHERE nid = %d", $node->nid)); $radio_options[1] = t('Enabled'); if ($has_signups) { $radio_options[0] = t('Disabled, but save existing signup information'); $radio_options[2] = t('Disabled, and remove all signup information') .' ('. t('This can not be undone, use with extreme caution!') .')'; } else { $radio_options[0] = t('Disabled'); } // Figure out what the default selection for signups should be. if (isset($node->signup)) { $default_option = $node->signup; } else { $default_option = $signup_type_default == 'enabled_on' ? 1 : 0; } if ($default_option == 1 ) { $hint = t('If enabled, you can control whether users may sign up by visiting the !signups tab and toggling if signups are %open or %closed for this @node_type.', array('!signups' => l(t('Signups'), 'node/'. $node->nid .'/signups'), '%open' => t('open'), '%closed' => t('closed'), '@node_type' => strtolower(node_get_types('name', $form['type']['#value'])))); } else { $hint = ''; } // Add the form element to toggle if signups are allowed. $form['signup']['signup_enabled'] = array( '#type' => 'radios', '#options' => $radio_options, '#default_value' => $default_option, '#description' => $hint .'
'. t('If disabled, all of the other signup settings will be ignored.') .'
', '#prefix' => '
', '#suffix' => '
', ); // If JS is enabled, signup.css will hide all the settings on page // load if signups aren't enabled on this node. $settings_class = "signup-node-settings"; if ($default_option != 1 ) { $settings_class .= " js-hide"; } // Add the actual settings. We wrap this in a div to make it easy // to use jQuery to hide these settings when signups are disabled. drupal_add_js(drupal_get_path('module', 'signup') .'/signup.js'); drupal_add_css(drupal_get_path('module', 'signup') .'/signup.css'); $form['signup']['node_settings'] = array( '#prefix' => '
', '#suffix' => '
', ); $form['signup']['node_settings']['settings'] = _signup_admin_form($node); } } /** * Submits the cancel signup form * * @ingroup signup_core * @param $form_id The ID of the form being submitted. * @param $form_values The constructed form values array of the submitted form. */ function signup_form_cancel_submit($form_id, $form_values) { signup_cancel_signup($form_values['uid'], $form_values['nid'], $form_values['signup_anon_mail']); } /** * Executes the user signup form * * @ingroup signup_core * @param $form_id The ID of the form being submitted. * @param $form_values The constructed form values array of the submitted form. */ function signup_form_submit($form_id, $form_values) { if (isset($form_values['signup_username'])) { $account = user_load(array('name' => $form_values['signup_username'])); $form_values['uid'] = $account->uid; } signup_sign_up_user($form_values); } /** * Validates the email address on the anonymous user signup form. * * @param $form * Form array for the anonymous user email field. * @param $nid * Node id of the node the user is trying to signup for. */ function signup_form_validate_anon($form, $nid) { signup_validate_anon_email($nid, $form['#value'], 'signup_anon_mail'); } /** * Validates the username on the admin form to signup another user. * * @param $form * Form array for the username field. * @param $nid * Node id of the node the user is being signed up for. */ function signup_form_validate_username($form, $nid) { $username = $form['#value']; $account = user_load(array('name' => $username)); if (empty($account)) { form_error($form, t('User %username does not exist.', array('%username' => $username))); } elseif (!user_access('sign up for content', $account)) { form_error($form, t('User !user does not have permission to sign up.', array('!user' => theme('username', $account)))); } elseif (db_result(db_query("SELECT COUNT(*) FROM {signup_log} WHERE uid = %d AND nid = %d", $account->uid, $nid)) > 0) { $node = node_load($nid); form_error($form, t('User !user is already signed up for %title', array('!user' => theme('username', $account), '%title' => $node->title))); } } /** * @defgroup signup_nodeapi Functions for nodeapi integration */ /** * hook_nodeapi implementation * * @ingroup signup_nodeapi */ function signup_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { global $form_values; $signup_type_default = variable_get('signup_node_default_state_'. $node->type, 'disabled'); switch ($op) { case 'insert': if (isset($form_values['signup_enabled'])) { if ($form_values['signup_enabled'] == 1) { $values = array( $node->nid, $form_values['signup_forwarding_email'], $form_values['signup_send_confirmation'], $form_values['signup_confirmation_email'], $form_values['signup_send_reminder'], $form_values['signup_reminder_days_before'], $form_values['signup_reminder_email'], $form_values['signup_close_signup_limit'], ); } } else if ($signup_type_default == 'enabled_on') { // The form doesn't include any information about signups, but // the node type is signup-enabled. This would happen if a // user without any signup admin permissions creates a node // that has been signup-enabled based on the node type. In // this case, we use the site-wide default signup settings. $defaults = db_fetch_array(db_query("SELECT * from {signup} WHERE nid = 0")); $values = array( $node->nid, $defaults['forwarding_email'], $defaults['send_confirmation'], $defaults['confirmation_email'], $defaults['send_reminder'], $defaults['reminder_days_before'], $defaults['reminder_email'], $defaults['close_signup_limit'], ); } if (isset($values)) { db_query("INSERT INTO {signup} (nid, forwarding_email, send_confirmation, confirmation_email, send_reminder, reminder_days_before, reminder_email, close_signup_limit) VALUES (%d, '%s', %d, '%s', %d, %d, '%s', %d)", $values); } break; case 'update': if (isset($form_values['signup_enabled'])) { $has_signup_record = db_result(db_query('SELECT COUNT(*) FROM {signup} WHERE nid = %d', $node->nid)); switch ($form_values['signup_enabled']) { case 1: // Enabled $limit_changed = false; if ($has_signup_record) { // See if the limit is going to change, since if it did, // we might have to change the status, too. $cur_limit = db_result(db_query("SELECT close_signup_limit FROM {signup} WHERE nid = %d", $node->nid)); $limit_changed = $cur_limit != $node->signup_close_signup_limit; db_query("UPDATE {signup} SET forwarding_email = '%s', send_confirmation = %d, confirmation_email = '%s', send_reminder = %d, reminder_days_before = %d, reminder_email = '%s', close_signup_limit = %d WHERE nid = %d", $node->signup_forwarding_email, $node->signup_send_confirmation, $node->signup_confirmation_email, $node->signup_send_reminder, $node->signup_reminder_days_before, $node->signup_reminder_email, $node->signup_close_signup_limit, $node->nid ); } else { db_query("INSERT INTO {signup} (nid, forwarding_email, send_confirmation, confirmation_email, send_reminder, reminder_days_before, reminder_email, close_signup_limit) VALUES (%d, '%s', %d, '%s', %d, %d, '%s', %d)", $node->nid, $node->signup_forwarding_email, $node->signup_send_confirmation, $node->signup_confirmation_email, $node->signup_send_reminder, $node->signup_reminder_days_before, $node->signup_reminder_email, $node->signup_close_signup_limit ); } if (_signup_event_completed($node) && $node->signup_status) { // If this is an event, and it's already past the close // in advance time, and signups are still open, close // them now (and don't consider the limit for changing // the status). signup_close_signup($node->nid); drupal_set_message(t('Event start time is already past the signup close-in-advance time, signups now closed.')); } else if ($limit_changed) { _signup_check_limit($node, 'limit'); } break; case 2: // Disabled, and delete {signup_log}, too db_query("DELETE FROM {signup_log} WHERE nid = %d", $node->nid); // No break, fall through and remove from {signup} too. case 0: // Disabled, but leave {signup_log} alone if ($has_signup_record) { db_query("DELETE FROM {signup} WHERE nid = %d", $node->nid); } break; } } break; case 'delete': // Clean up the signup tables for the deleted node. db_query("DELETE FROM {signup} WHERE nid = %d", $node->nid); db_query("DELETE FROM {signup_log} WHERE nid = %d", $node->nid); break; case 'load': // Check for a signup for this node. // If it's a new node, load the defaults. $result = db_query("SELECT * FROM {signup} WHERE nid = %d", ($node->nid ? $node->nid : 0)); // Load signup data for both new nodes w/ enabled node types, // and any existing nodes that are already signup enabled. if ((!$node->nid && $signup_type_default == 'enabled_on') || ($node->nid && db_num_rows($result))) { $signup = db_fetch_object($result); $node->signup = 1; $node->signup_forwarding_email = $signup->forwarding_email; $node->signup_send_confirmation = $signup->send_confirmation; $node->signup_confirmation_email = $signup->confirmation_email; $node->signup_send_reminder = $signup->send_reminder ; $node->signup_reminder_days_before = $signup->reminder_days_before; $node->signup_reminder_email = $signup->reminder_email; $node->signup_close_signup_limit = $signup->close_signup_limit; $node->signup_status = $signup->status; if ($node->nid) { $node->signup_total = db_result(db_query("SELECT COUNT(*) FROM {signup_log} WHERE nid = %d", $node->nid)); } } else { $node->signup = 0; } break; case 'view': // If this is a signup node, figure out what (if anything) to print. // Only include any of this if we're trying to view the node as // a page, not during the view from comment validation, etc. if ($page && _signup_needs_output($node) && variable_get('signup_form_location', 'node') == 'node') { $output = _signup_node_output($node); if (!empty($output)) { // Save into a node property for retrieval from the theme layer. $node->signup_view = $output; // Store the data into the content array, for default display. $node->content['signup'] = array( '#value' => $output, '#weight' => 10, ); } } break; } } /** * Generate all the signup-related output for a given node. * * Because of the global setting to control if the signup details and form * appear at the bottom of the node or on a separate tab, this function is * shared by multiple callers. * * @param $node * The fully loaded node object. * @param $type * The kind of output would we render: can be either 'node' or 'tab'. * * @return * The fully rendered HTML for all signup-related forms and info. * * @see signup_nodeapi() * @see signup_node_tab() * * @todo This needs to be much more theme-friendly. * */ function _signup_node_output($node, $type = 'node') { global $user; $output = ''; // The node has been closed for signups, and the user has // signup permissions. Let them know it's closed. if (!$node->signup_status) { if (user_access('sign up for content')) { $output = '

'. t('Signups closed for this event') .'

'; // If they're logged in and already signed up, show their current // signup info and give them the option to cancel. if ($user->uid) { $result = db_query("SELECT signup_time, form_data FROM {signup_log} WHERE uid = %d AND nid = %d", $user->uid, $node->nid); if (db_num_rows($result)) { $signup_info = db_fetch_object($result); $output .= _signup_print_current_signup($node, $signup_info); } } } } else { $signup_type = 'auth'; if ($user->uid == 0) { // This is an anonymous user. If they have signup permissions, // then build the anon portion of the sigup form. If not, then // display the login link. $login_array = array( '!login' => l(t('login'), 'user/login', array(), drupal_get_destination()), '!register' => l(t('register'), 'user/register', array(), drupal_get_destination()), ); if (user_access('sign up for content')) { $needs_signup_form = TRUE; $signup_type = 'anon'; } else { $needs_signup_form = FALSE; $output .= '
'. t('Please !login or !register to sign up for this event.', $login_array) .'
'; } } else { // See if the user is already signed up for this node. $result = db_query("SELECT signup_time, form_data FROM {signup_log} WHERE uid = %d AND nid = %d", $user->uid, $node->nid); $needs_signup_form = db_num_rows($result) == 0; } if ($needs_signup_form) { // User isn't signed up, so check to make sure they have signup // permissions, and if so, print the themed signup form. if (user_access('sign up for content')) { $fieldset = $type == 'node' ? TRUE : FALSE; $output = drupal_get_form('signup_form', $node, $signup_type, $fieldset); } } else if ($user->uid !== 0 && isset($result)) { // The authenticated user is already signed up, so print a table // of their signup data, and give them the option to cancel. $signup_info = db_fetch_object($result); $output .= _signup_print_current_signup($node, $signup_info); } } // If the user has the view signups perm, display the current signups. // Pull all users signed up for this event, and start table creation. if (user_access('view all signups')) { $registered_signups = db_query("SELECT u.uid, u.name, s.signup_time, s.form_data FROM {signup_log} s INNER JOIN {users} u ON u.uid = s.uid WHERE s.nid = %d AND u.uid <> 0", $node->nid); $anon_signups = db_num_rows(db_query("SELECT anon_mail FROM {signup_log} WHERE nid = %d AND uid = 0", $node->nid)); $header = array(array('data' => t('!users signed up', array('!users' => format_plural((db_num_rows($registered_signups) + $anon_signups), '1 individual', '@count individuals'))))); $rows = array(); while ($signed_up_user = db_fetch_object($registered_signups)) { $rows[] = array(theme('username', $signed_up_user)); } if ($anon_signups) { $rows[] = array(t('!count anonymous', array('!count' => $anon_signups))); } $output .= theme('table', $header, $rows); } return $output; } /** * Helper function that determines if a given node should have any * signup-related output. * * @param $node A fully loaded node object. * @return TRUE if this node should have signup output, FALSE if not. * * @see signup_nodeapi() */ function _signup_needs_output($node) { if (!$node->signup) { // Not signup enabled at all. return FALSE; } $suppress = module_invoke_all('signup_suppress', $node); if (in_array(TRUE, $suppress)) { // Someone doesn't want signup details printed. return FALSE; } return TRUE; } function signup_node_tab($node) { drupal_set_title(check_plain($node->title)); return _signup_node_output($node, 'tab'); } function _signup_print_current_signup($node, $signup_info) { $form_data = unserialize($signup_info->form_data); $header = array(array('data' => t('Your signup information'), 'colspan' => 2)); $rows = array(); if (is_array($form_data)) { $rows += signup_build_signup_data($form_data, 'table'); } $output = ''; if (!empty($rows)) { $output .= theme('table', $header, $rows); } if (user_access('cancel own signups')) { $output .= drupal_get_form('signup_form_cancel', $node); } return $output; } /** * @defgroup signup_callback * Functions which are the menu callbacks for this module. */ /** * Builder function for the signup form * @ingroup signup_callback * * @param $node * The fully loaded node object. * @param $signup_type * Determines what kind of signup to generate a form for. Possible values: * 'auth' -- regular authenticated user signup form * 'anon' -- anonymous user signup form (includes required email field). * 'admin' -- admin form to signup another user (includes user selector). * @param $fieldset * Boolean that indicates if the signup form should be in a fieldset. */ function signup_form($node, $signup_type = 'auth', $fieldset = TRUE) { global $user; include_once(SIGNUP_PATH .'/signup.theme'); $form = array(); $form['nid'] = array('#type' => 'value', '#value' => $node->nid); $form['uid'] = array('#type' => 'value', '#value' => $user->uid); if ($fieldset) { $form['collapse'] = array( '#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => variable_get('signup_fieldset_collapsed', 1), ); if ($signup_type == 'admin') { $form['collapse']['#title'] = t('Sign up another user'); } else { $form['collapse']['#title'] = t('Sign up for @title', array('@title' => $node->title)); } } else { $form['collapse'] = array(); } $signup_form = array(); if ($signup_type == 'anon') { $anon_form = array(); $anon_form['signup_anon_mail'] = array( '#type' => 'textfield', '#title' => t('Email'), '#description' => t('An e-mail address is required for users who are not registered at this site. If you are a registered user at this site, please !login to sign up for this event.', $login_array), '#size' => 40, '#validate' => array('signup_form_validate_anon' => array($node->nid)), '#maxlength' => 255, '#required' => TRUE, ); $signup_form += $anon_form; } elseif ($signup_type == 'admin') { $admin_form = array(); $admin_form['signup_username'] = array( '#title' => t('Username'), '#type' => 'textfield', '#autocomplete_path' => 'user/autocomplete', '#maxlength' => USERNAME_MAX_LENGTH, '#validate' => array('signup_form_validate_username' => array($node->nid)), '#size' => 40, '#weight' => -1, '#required' => TRUE, ); $signup_form += $admin_form; } // Build the themed signup form for this site and include that. $signup_themed_form = theme('signup_user_form'); if ($signup_type == 'admin') { // Special case hack for the default signup form, where the current // username is being filled in as the default for the 'Name' field. if (!empty($signup_themed_form['signup_form_data']['Name']['#default_value'])) { unset($signup_themed_form['signup_form_data']['Name']['#default_value']); } } $signup_form += $signup_themed_form; $form['collapse']['signup_user_form'] = $signup_form; $form['collapse']['submit'] = array( '#type' => 'submit', '#value' => t('Sign up'), ); return $form; } /** * Builder function for the cancel signup form * @ingroup signup_callback */ function signup_form_cancel($node) { global $user; $form['nid'] = array('#type' => 'value', '#value' => $node->nid); $form['uid'] = array('#type' => 'value', '#value' => $user->uid); $form['submit'] = array('#type' => 'submit', '#value' => t('Cancel signup')); return $form; } /** * Prints the admin signup overview page located at admin/content/signup * @ingroup signup_callback */ function signup_admin_page() { drupal_add_css(drupal_get_path('module', 'signup') .'/signup.css'); $output = drupal_get_form('signup_filter_status_form'); $output .= drupal_get_form('signup_admin_form'); return $output; } function signup_filter_status_form() { $options = array( 'all' => t('All'), 'open' => t('Open'), 'closed' => t('Closed'), ); if (empty($_SESSION['signup_status_filter'])) { $_SESSION['signup_status_filter'] = 'all'; } $form['filter'] = array( '#type' => 'select', '#title' => t('Filter by signup status'), '#options' => $options, '#default_value' => $_SESSION['signup_status_filter'], ); $form['submit'] = array('#type' => 'submit', '#value' => t('Filter')); $form['#redirect'] = FALSE; return $form; } function theme_signup_filter_status_form($form) { return '
'. drupal_render($form) .'
'; } function signup_filter_status_form_submit($form_id, $form_values) { $_SESSION['signup_status_filter'] = $form_values['filter']; } function signup_admin_form() { // Figure out if the current user has permission to use signup broadcast. $access_broadcast = user_access('email all signed up users'); $header = array( array('data' => t('Title'), 'field' => 'n.title', 'sort' => 'asc'), array('data' => t('Signups'), 'field' => 'signup_total'), array('data' => t('Limit'), 'field' => 'signup_close_signup_limit'), array('data' => t('Status'), 'field' => 'signup_status'), array('data' => t('Operations')), ); if (function_exists('signup_admin_form_header') && is_array($extra_header = signup_admin_form_header())) { array_unshift($header, $extra_header); } list($sql, $sql_count) = signup_admin_form_sql(); $form['header']['#value'] = $header; $sql .= tablesort_sql($header); $result = pager_query($sql, 25, 0, $sql_count); // Loop through the signup nodes, and generate our form elements while ($signup_event = db_fetch_object($result)) { $row = array(); if (function_exists('signup_admin_form_extra')) { $row['start'] = signup_admin_form_extra($signup_event); } // Instead of duplicating the logic from the node/N/signups admin // form, we just call that form builder here and lift the elements // we need directly from that. $node_admin_form = signup_admin_node_form($signup_event); $row['title'] = array( '#type' => 'markup', '#value' => l($signup_event->title, "node/$signup_event->nid"), ); $row['status'] = $node_admin_form['status']; $row['total'] = array( '#type' => 'markup', '#value' => $signup_event->signup_total, ); $row['limit'] = $node_admin_form['limit']; $op_links = l(t('View signups'), "node/$signup_event->nid/signups"); if ($access_broadcast) { $op_links .= '
'; $op_links .= l(t('Signup broadcast'), "node/$signup_event->nid/signup-broadcast", array('title' => t('Send an email message to all users who signed up.'))); } $row['operations'] = array( '#type' => 'markup', '#value' => $op_links, ); $form['nids'][$signup_event->nid] = $row; } $form['#tree'] = true; $form['submit'] = array( '#type' => 'submit', '#value' => t('Update'), ); return $form; } function signup_admin_form_sql() { $admin_common_sql = array( 'primary' => '{signup} s', 'fields' => array( 'n.nid', 'n.title', 's.status AS signup_status', 'COUNT(s_l.nid) AS signup_total', 's.close_signup_limit AS signup_close_signup_limit', ), 'group_by' => array( 'n.nid', 'n.title', 'signup_status', 'signup_close_signup_limit', ), 'joins' => array( 'INNER JOIN {node} n ON n.nid = s.nid', 'LEFT JOIN {signup_log} s_l ON s.nid = s_l.nid', ), ); $type = $_SESSION['signup_status_filter']; if ($type == 'open') { $filter_status = 1; } else if ($type == 'closed') { $filter_status = 0; } if (isset($filter_status)) { $admin_common_sql['where'] = array("s.status = $filter_status"); } // Get the right query elements from the currently installed backend if (function_exists('signup_admin_sql')) { $admin_sql = signup_admin_sql(); } else { $admin_sql = array(); } // Build the main query. $sql = _signup_build_query($admin_common_sql, $admin_sql); // Construct the proper pager query using just the WHERE clauses (if any). $where = array_merge( (!empty($admin_common_sql['where']) ? $admin_common_sql['where'] : array()), (!empty($admin_sql['where']) ? $admin_sql['where'] : array()) ); $sql_count = "SELECT COUNT(s.nid) FROM {signup} s"; if (!empty($where)) { $sql_count .= ' WHERE '. implode(' AND ', $where); } return array(db_rewrite_sql($sql), db_rewrite_sql($sql_count, 's')); } function theme_signup_admin_form($form) { if (!isset($form['nids'])) { $type = $_SESSION['signup_status_filter']; switch ($type) { case 'open': $filter = t('open'); break; case 'closed': $filter = t('closed'); break; default: $filter = t('enabled'); break; } return t('No content is currently !status for signups.', array('!status' => $filter)); } foreach ($form['nids'] as $nid => $node_form) { if (!is_numeric($nid)) { continue; } $row = array(); if (isset($node_form['start'])) { $row[] = drupal_render($form['nids'][$nid]['start']); } $row[] = drupal_render($form['nids'][$nid]['title']); $row[] = drupal_render($form['nids'][$nid]['total']); $row[] = drupal_render($form['nids'][$nid]['limit']); $row[] = drupal_render($form['nids'][$nid]['status']); $row[] = drupal_render($form['nids'][$nid]['operations']); $rows[] = $row; } $header = $form['header']['#value']; unset($form['header']); $output = theme('table', $header, $rows, array('style' => 'width:100%')); $output .= drupal_render($form); $pager = theme('pager', NULL, 25, 0); if (!empty($pager)) { $output .= $pager; } return $output; } function signup_admin_form_submit($form_id, $form_values) { foreach ($form_values['nids'] as $nid => $values) { $values['nid'] = $nid; signup_admin_node_form_submit($form_id, $values); } } /** * Callback function for canceling signups * @ingroup signup_callback */ function signup_cancel_signup($uid, $nid, $anon_mail = NULL) { // Delete the selected user from the log table. if ($anon_mail) { db_query("DELETE FROM {signup_log} WHERE anon_mail = '%s'", $anon_mail); } else { db_query('DELETE FROM {signup_log} WHERE uid = %d AND nid = %d', $uid, $nid); } $node = node_load($nid); $node->signup_total--; module_invoke_all('signup_cancel', $node, $uid); drupal_set_message(t('Signup to !title cancelled.', array('!title' => l($node->title, "node/$node->nid")))); _signup_check_limit($node, 'total'); } /** * Callback function for closing signups * @ingroup signup_callback */ function signup_close_signup($nid, $cron = 'no') { db_query("UPDATE {signup} SET status = 0 WHERE nid = %d", $nid); if ($cron == 'no') { $node = node_load($nid); foreach (module_implements('signup_close') as $module) { $function = $module .'_signup_close'; $function($node); } watchdog('signup', t('Signups closed for %title.', array('%title' => $node->title)), WATCHDOG_NOTICE, l(t('view'), 'node/'. $nid)); } } /** * Callback function for reopening signups * @ingroup signup_callback */ function signup_open_signup($nid, $cron = 'no') { db_query("UPDATE {signup} SET status = 1 WHERE nid = %d", $nid); if ($cron == 'no') { $node = node_load(array('nid' => $nid)); foreach (module_implements('signup_open') as $module) { $function = $module .'_signup_open'; $function($node); } watchdog('signup', t('Signups reopened for %title.', array('%title' => $node->title)), WATCHDOG_NOTICE, l(t('view'), 'node/'. $nid)); } } /** * Form builder for the settings page under admin/setttings/signup */ function signup_settings_page() { $form['signup_close_early'] = array( '#title' => t('Close x hours before'), '#type' => 'textfield', '#default_value' => variable_get('signup_close_early', 1), '#size' => 5, '#maxlength' => 10, '#description' => t('The number of hours before the event which signups will no longer be allowed. Use negative numbers to close signups after the event start (example: -12).'), ); $form['node_defaults'] = array( '#type' => 'fieldset', '#title' => t('Default signup information'), '#description' => t('New signup-enabled nodes will start with these settings.'), '#collapsible' => true, ); $form['node_defaults']['_signup_admin_form'] = _signup_admin_form($node); $form['adv_settings'] = array( '#type' => 'fieldset', '#title' => t('Advanced settings'), '#collapsible' => true, '#collapsed' => true, ); $form['adv_settings']['signup_form_location'] = array( '#title' => t('Location of the signup form and related information'), '#type' => 'radios', '#options' => array( 'node' => t('At the bottom of each node'), 'tab' => t('On a separate %sign_up tab', array('%sign_up' => t('Sign up'))), ), '#default_value' => variable_get('signup_form_location', 'node'), '#description' => t('On every signup-enabled node, users with permission to sign up will be presented with a form. Additionally, users the %view_signups permission will see a list of all users who have signed up. This setting controls where this information should be displayed: either directly on the node itself, or on a separate tab.', array('%view_signups' => t('view all signups'))), ); $form['adv_settings']['signup_fieldset_collapsed'] = array( '#title' => t('Default fieldset behavior for per-node signup form'), '#type' => 'radios', '#options' => array(1 => t('Collapsed'), 0 => t('Expanded')), '#default_value' => variable_get('signup_fieldset_collapsed', 1), '#description' => t('If the signup form is included at the bottom of each node, the signup form will be encapsulated in a collapsible fieldset. This setting controls if that fieldset is expanded or collapsed by default.'), ); // Use our own submit handler, so we can do some processing before // we hand control to system_settings_form_submit. $form['#submit']['signup_settings_page_submit'] = array(); return system_settings_form($form); } /** * Submits the signup settings form * * @param $form_id The ID of the form being submitted. * @param $form_values The constructed form values array of the submitted form. */ function signup_settings_page_submit($form_id, $form_values) { $op = isset($form_values['op']) ? $form_values['op'] : ''; if ($op == t('Save configuration') && db_num_rows(db_query('SELECT nid FROM {signup} WHERE nid = 0'))) { db_query("UPDATE {signup} SET forwarding_email = '%s', send_confirmation = %d, confirmation_email = '%s', send_reminder = %d, reminder_days_before = %d, reminder_email = '%s', close_signup_limit = %d WHERE nid = 0", $form_values['signup_forwarding_email'], $form_values['signup_send_confirmation'], $form_values['signup_confirmation_email'], $form_values['signup_send_reminder'], $form_values['signup_reminder_days_before'], $form_values['signup_reminder_email'], $form_values['signup_close_signup_limit'] ); } else { require_once 'signup.install'; db_query("DELETE FROM {signup} WHERE nid = 0"); signup_insert_default_signup_info(); } // Now, remove all the settings we just processed from our copy of // $form_values, so system_settings_form_submit() doesn't see them. $settings = array( 'signup_forwarding_email', 'signup_send_confirmation', 'signup_confirmation_email', 'signup_send_reminder', 'signup_reminder_days_before', 'signup_reminder_email', 'signup_close_signup_limit', ); foreach ($settings as $setting) { unset($form_values[$setting]); } // Remove the hidden element from _signup_admin_form(), too. unset($form_values['signup']); // Finally, let system_settings_form_submit() do its magic with the // rest of the settings. system_settings_form_submit($form_id, $form_values); } /** * Returns an array of node titles with links for all events the * specified user has signed up for. */ function signup_list_user_signups($uid) { $titles = array(); // We don't want to return anything for anon users. if ($uid != 0) { $sql = signup_list_user_signups_sql(); // Pull all open signup nodes for this user. $result = db_query(db_rewrite_sql($sql), $uid); while ($node = db_fetch_array($result)) { $titles[$node['nid']] = l($node['title'], 'node/'. $node['nid']); } } return $titles; } /** * Signs up a user to a node. * * NOTE: other modules can call this function. To do so, $signup_form * must be as follows: * * $signup_form['nid'] : nid of the node to which the user will be signed up * $signup_form['uid'] : uid of the user to sign up * $signup_form['signup_anon_mail'] : Optional. An email address of an * anonymous user to sign up. Only include this if the user is not * already registered with the site. $signup_form['uid'] will * automatically be set to 0 if this element is passed in. NOTE: It's * highly recommended to call the signup_validate_anon_email * function in the external module's validation cycle or perform * that function's validation logic prior to passing in this element! * $signup_form['signup_form_data'] : an array of key/value pairs -- * key is the data category, value is the user input */ function signup_sign_up_user($signup_form) { $node = node_load($signup_form['nid']); // Since this is an API call, we need to validate that there are no // duplicate signups being generated, even though through the usual // UI, there's no way to reach this function if it's a duplicate. // How to find duplicates is different for anonymous and // authenticated signups. if (!empty($signup_form['signup_anon_mail'])) { // Ensure the uid is 0 for anonymous signups, even if it's not duplicate. $signup_form['uid'] = 0; // Now, see if this email is already signed-up. if (db_num_rows(db_query("SELECT anon_mail FROM {signup_log} WHERE anon_mail = '%s' AND nid = %d", $signup_form['signup_anon_mail'], $node->nid))) { drupal_set_message(t('Anonymous user %email is already signed up for %title', array('%email' => $signup_form['signup_anon_mail'], '%title' => $node->title), 'error')); return FALSE; } } else { // This query does the JOIN on {users} so we can avoid a full // user_load() just so theme('username') can have the data it // needs for the error message we might print out. $query = db_query("SELECT sl.uid, u.name FROM {signup_log} sl INNER JOIN {users} u ON sl.uid = u.uid WHERE sl.uid = %d AND sl.nid = %d", $signup_form['uid'], $signup_form['nid']); if (db_num_rows($query)) { $user = db_fetch_object($query); drupal_set_message(t('User !user is already signed up for %title', array('!user' => theme('username', $user), '%title' => $node->title)), 'error'); return FALSE; } } // If we made it this far, we're going to need the full $user object. $user = user_load(array('uid' => $signup_form['uid'])); if (user_access('sign up for content') && $node->signup_status) { // Grab the current time once, since we need it in a few places. $curtime = time(); // Allow other modules to inject data into the user's signup data. $extra = module_invoke_all('signup_sign_up', $node); $signup_form_data = serialize(array_merge($signup_form['signup_form_data'], $extra)); // Figure out if confirmation or reminder emails will be sent and // inform the user. $confirmation_email = $node->signup_send_confirmation ? ' '. t('You will receive a confirmation email shortly which contains further event information.') : ''; $reminder_email = $node->signup_send_reminder ? ' '. t('You will receive a reminder email !number !days before the event.', array('!number' => $node->signup_reminder_days_before, '!days' => format_plural($node->signup_reminder_days_before, 'day', 'days'))) : ''; // Insert the user into the signup_log. db_query("INSERT INTO {signup_log} (uid, nid, anon_mail, signup_time, form_data) VALUES (%d, %d, '%s', %d, '%s')", $signup_form['uid'], $signup_form['nid'], $signup_form['signup_anon_mail'], $curtime, $signup_form_data); // Format the start time, and compose the user's signup data for // later use in the emails. $starttime = signup_format_date($node); $signup_data_array = array(); if (isset($signup_form['signup_form_data'])) { $signup_form_data = signup_build_signup_data($signup_form['signup_form_data'], 'email'); $signup_data = t('SIGNUP INFORMATION') ."\n\r\n\r". $signup_form_data; } // Determine if this is an anon signup or not, and get the // appropriate email address to use. $user_mail = $signup_form['signup_anon_mail'] ? $signup_form['signup_anon_mail'] : $user->mail; // This is not used for web display, only email, so the output is // not being sanitized and linked as we would for web output. $trans = array( "%event" => $node->title, "%time" => $starttime, "%username" => $user->name, "%useremail" => $user_mail, "%info" => $signup_data ); $from = variable_get('site_mail', 'noadmin@noadmin.com'); // If a confirmation is to be sent, compose the mail message, // translate the string substitutions, and send it. if ($node->signup_send_confirmation && $user_mail) { $subject = t('Signup confirmation for event: !event', array('!event' => $node->title)); $message = strtr($node->signup_confirmation_email, $trans); drupal_mail('signup_confirmation_mail', $user_mail, $subject, $message, $from); } // If a forwarding email is to be sent, compose the mail message, // translate the string substitutions, and send it. if ($node->signup_forwarding_email) { $header = array('From' => t('New Event Signup') ."<$from>"); $subject = t('Signup confirmation for event: !title', array('!title' => $node->title)); $message = t('The following information was submitted as a signup for !title', array('!title' => $node->title)) . "\n\r". t('Date/Time: !time', array('!time' => $starttime)) .":\n\r\n\r". "\n\r". t('Username:') . $user->name . "\n\r". t('Email:') . $user_mail ."\n\r\n\r". $signup_data; drupal_mail('signup_forwarding_mail', $node->signup_forwarding_email, $subject, $message, $from, $header); } drupal_set_message(t('Signup to !title confirmed.', array('!title' => l($node->title, "node/$node->nid"))) . $confirmation_email . $reminder_email); $node->signup_total++; if ($node->signup_close_signup_limit) { _signup_check_limit($node, 'total'); } } else { drupal_access_denied(); } } /** * Prints the signup details for a single node when the signups tab is clicked * @ingroup signup_callback */ function signup_node_admin_page($node) { drupal_set_title(check_plain($node->title)); // Administrative table to control signups for this node. $output = '

'. t('Signup summary') .'

'; $output .= drupal_get_form('signup_admin_node_form', $node); $output .= '

'. t('Signup details') .'

'; $header = array( t('Name/Signup time'), t('Extra information'), t('Operations'), ); // Pull all user signed up for this event, and start table creation. $result = db_query("SELECT u.uid, u.name, s.anon_mail, s.signup_time, s.form_data FROM {signup_log} s INNER JOIN {users} u ON u.uid = s.uid WHERE s.nid =%d", $node->nid); $rows = array(); // Get default timezone offset for the site. $offset = intval(variable_get('date_default_timezone', 0)); // Loop through the users, unserializing their user data. while ($signed_up_user = db_fetch_object($result)) { $table_data = array(); $form_data = unserialize($signed_up_user->form_data); // Compose the user data. $signup_form_data = signup_build_signup_data($form_data); // The username and the unique form identifier are different for // anon signups and registered user signups. For registered users, // provide a link to the user profile, and use the uid as the // identifier. For anon, use the user's email address as the // identifier and name. if ($signed_up_user->uid == 0) { $username = check_plain($signed_up_user->anon_mail); $id = $signed_up_user->anon_mail; } else { $username = theme('username', $signed_up_user); $id = $signed_up_user->uid; } // Build the row for this user. $rows[] = array( $username .'
'. gmdate(variable_get('signup_date_string', 'M jS, g:i A'), $signed_up_user->signup_time + $offset), $signup_form_data, drupal_get_form('signup_user_cancel_form_'. $id, $id, $node->nid, $signed_up_user->uid, $signed_up_user->anon_mail) ); } $output .= theme('table', $header, $rows); // Add a form to allow the administrator to signup other users. $output .= drupal_get_form('signup_form', $node, 'admin'); return $output; } function theme_signup_admin_node_form($form) { $row = array( drupal_render($form['status']), drupal_render($form['total']), drupal_render($form['limit']), drupal_render($form), ); $header = array( t('Status'), t('Total'), t('Limit'), t('Operations'), ); return theme('table', $header, array($row)); } function signup_admin_node_form($node) { if ($node->signup_close_signup_limit && $node->signup_total >= $node->signup_close_signup_limit) { $form['status'] = array( '#value' => t('Closed (limit reached)'), ); } else { $form['status'] = array( '#type' => 'select', '#options' => array(0 => t('Closed'), 1 => t('Open')), '#default_value' => $node->signup_status, ); } $form['limit'] = array( '#type' => 'textfield', '#default_value' => $node->signup_close_signup_limit, '#size' => 4, '#maxlength' => 8, ); $form['total'] = array( '#value' => $node->signup_total, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Update'), ); $form['nid'] = array( '#type' => 'value', '#value' => $node->nid, ); return $form; } function signup_admin_node_form_submit($form_id, $form_values) { $nid = $form_values['nid']; $node = node_load($nid); $limit_status = 0; if (isset($form_values['limit']) && ($form_values['limit'] != $node->signup_close_signup_limit)) { db_query("UPDATE {signup} SET close_signup_limit = %d WHERE nid = %d", $form_values['limit'], $nid); $node->signup_close_signup_limit = $form_values['limit']; $limit_status = _signup_check_limit($node, 'limit'); } // Only consider the form's status value if the signup limit didn't // touch the status already. if (!$limit_status && isset($form_values['status']) && ($form_values['status'] != $node->signup_status)) { if ($form_values['status']) { signup_open_signup($nid); drupal_set_message(t('Signups opened for !title.', array('!title' => l($node->title, "node/$node->nid")))); } else { signup_close_signup($nid); drupal_set_message(t('Signups closed for !title.', array('!title' => l($node->title, "node/$node->nid")))); } } } /** * Checks the signup limit for a given node, and sees if a change in * either the limit or total # of signups should result in a change in * signup status (open vs. closed) and prints a message indicating * what happened. * * @param $node * The node to update, can be a full $node object or a numeric nid. * @param $type * String indicating what changed -- can be either 'limit' or 'total'. * * @return * A flag indicating what change (if any) to the signup status was * required due to the change in limit. 0 if there was no change, -1 * if signups are now closed, and 1 if signups are now open. */ function _signup_check_limit($node, $type) { $status_change = 0; if (is_numeric($node)) { $node = node_load($node); } $node_link = l($node->title, "node/$node->nid"); $limit = $node->signup_close_signup_limit; if ($limit) { if ($node->signup_total >= $limit) { if ($node->signup_status) { signup_close_signup($node->nid); $status_change = -1; drupal_set_message(t('Signup limit reached for !title, signups closed.', array('!title' => $node_link))); } else if ($type == 'limit') { // This is a weird special case, where signups are already // closed, but the admin lowers the limit to the signup total // or lower. We need to print a message about this, and also // return -1 so that callers know signups must remain closed. drupal_set_message(t('Signup limit reached.')); $status_change = -1; } } else if (($node->signup_total < $limit) && (!$node->signup_status) && (!_signup_event_completed($node))) { signup_open_signup($node->nid); $status_change = 1; if ($type == 'limit') { drupal_set_message(t('Signup limit increased for !title, signups re-opened.', array('!title' => $node_link))); } else { drupal_set_message(t('Total signups for !title now below limit, signups re-opened.', array('!title' => $node_link))); } } else if ($type == 'limit') { drupal_set_message(t('Signup limit updated for !title.', array('!title' => $node_link))); } } else if ($type == 'limit') { // These checks should only happen if the limit was just changed... if (!$node->signup_status && !_signup_event_completed($node)) { signup_open_signup($node->nid); $status_change = 1; drupal_set_message(t('Signup limit removed for !title, signups now open.', array('!title' => $node_link))); } else { drupal_set_message(t('Signup limit removed for !title.', array('!title' => $node_link))); } } return $status_change; } /** * Implementation of hook_forms(). */ function signup_forms() { $args = func_get_args(); $args = $args[0]; $form_id = array_shift($args); if (strpos($form_id, 'signup_user_cancel_form') !== FALSE) { if ($form_id == 'signup_user_cancel_form_'. $args[0]) { array_shift($args); // Get rid of the extra uid arg. $forms[$form_id] = array( 'callback' => 'signup_user_cancel_form', 'callback arguments' => $args, ); return $forms; } } } function signup_user_cancel_form($nid, $uid, $anon_mail) { $form['#base'] = 'signup_form_cancel'; $form['nid'] = array('#type' => 'value', '#value' => $nid); $form['uid'] = array('#type' => 'value', '#value' => $uid); $form['signup_anon_mail'] = array('#type' => 'value', '#value' => $anon_mail); $form['submit'] = array('#type' => 'submit', '#value' => t('Cancel signup')); return $form; } /** * Validates an anonymous signup email. * * @param $nid The node the user is signing up for. * @param $anon_mail The anonymous email address to validate. * @param $name Optional. The form element being validated. * * @return Boolean. TRUE if the address validates, FALSE otherwise. */ function signup_validate_anon_email($nid, $anon_mail, $name = FALSE) { if (!valid_email_address($anon_mail)) { $message = 'Invalid email address entered for signup.'; } else if (db_num_rows(db_query("SELECT mail FROM {users} WHERE mail = '%s'", $anon_mail))) { $message = 'The email address entered belongs to a registered user.'; } else if (db_num_rows(db_query("SELECT anon_mail FROM {signup_log} WHERE anon_mail = '%s' AND nid = %d", $anon_mail, $nid))) { $message = 'The email address entered has already been used to sign up for this event.'; } // If there's no message, it's a valid email, so return success. if (!isset($message)) { return TRUE; } // Depending on how we were called, propagate the error accordinly. if ($name) { form_set_error($name, $message); } else { drupal_set_message($message, 'error'); } return FALSE; } /** * @defgroup signup_internal Internal module functions */ /** * Returns the form for the per-node signup settings. This is shared * by the settings page and the node edit page. * @ingroup signup_internal */ function _signup_admin_form($node) { $signup_token_description = t('Supported string substitutions: %event, %time, %username, %useremail, %info (user signup information).'); // Load the default admin form data for new nodes. if (!$node || !$node->signup) { $result = db_fetch_object(db_query("SELECT * FROM {signup} WHERE nid = 0")); $node->signup_forwarding_email = $result->forwarding_email; $node->signup_send_confirmation = $result->send_confirmation; $node->signup_confirmation_email = $result->confirmation_email; $node->signup_send_reminder = $result->send_reminder; $node->signup_reminder_days_before = $result->reminder_days_before; $node->signup_reminder_email = $result->reminder_email; $node->signup_close_signup_limit = $result->close_signup_limit; } $form['signup_forwarding_email'] = array( '#type' => 'textfield', '#title' => t('Send signups to'), '#default_value' => $node->signup_forwarding_email, '#size' => 40, '#maxlength' => 64, '#description' => t('Email address where notification of new signups will be sent. Leave blank for no notifications.'), ); $form['signup_send_confirmation'] = array( '#type' => 'checkbox', '#title' => t('Send confirmation'), '#default_value' => $node->signup_send_confirmation, ); $form['signup_confirmation_email'] = array( '#type' => 'textarea', '#title' => t('Confirmation email'), '#default_value' => $node->signup_confirmation_email, '#cols' => 40, '#rows' => 6, '#description' => t('Email sent to user upon signup.') .' '. $signup_token_description, ); // Define a sub-tree to wrap the next 2 form elements together in an // inline div for better display. $form['signup_reminder'] = array( '#prefix' => '
', '#suffix' => '
', ); $form['signup_reminder']['signup_send_reminder'] = array( '#type' => 'checkbox', '#title' => t('Send reminder'), '#default_value' => $node->signup_send_reminder, ); $options = array(); for ($i = 1; $i <= 60; $i++) { $options[$i] = $i; } $form['signup_reminder']['signup_reminder_days_before'] = array( '#type' => 'select', '#default_value' => $node->signup_reminder_days_before, '#options' => $options, '#suffix' => t('day(s) before event'), ); $form['signup_reminder_email'] = array( '#type' => 'textarea', '#title' => t('Reminder email'), '#default_value' => $node->signup_reminder_email, '#cols' => 40, '#rows' => 6, '#description' => t('Email sent to user as an event reminder.') .' '. $signup_token_description, ); $form['signup_close_signup_limit'] = array( '#type' => 'textfield', '#title' => t('Signup limit'), '#default_value' => $node->signup_close_signup_limit, '#size' => 4, '#maxlength' => 8, '#description' => t('Maximum number of users who can sign up before signups are automatically closed. If set to 0, there is no limit.'), ); $form['signup'] = array('#type' => 'hidden', '#value' => 1); return $form; } /** * Builds serialized user signup data into user-readable format. * * @param $data The serialized user signup data. * @param $type The type of formatting -- defaults to 'output'. * * @return For table formatting, an array of table rows, for output formatting, raw user data in divs. */ function signup_build_signup_data($data, $type = 'output') { switch ($type) { case 'table': static $rows = array(); // Loop through each first level element. foreach ($data as $key => $value) { // Element is nested, render it recursively. if (is_array($value)) { $rows[] = array('
'); signup_build_signup_data($value, 'table'); } else { $rows[] = array($key .':', check_plain($value)); } } return $rows; case 'output': $output = ''; // Loop through each first level element. foreach ($data as $key => $value) { // Element is nested, render it recursively. if (is_array($value)) { $output .= '
'. signup_build_signup_data($value) .'
'; } else { $output .= '
'. $key .': '. check_plain($value) .'
'; } } return $output; case 'email': $output = ''; // Loop through each first level element. foreach ($data as $key => $value) { // Element is nested, render it recursively. if (is_array($value)) { $output .= "\n\r". signup_build_signup_data($value, 'email') ."\n\r"; } else { $output .= $value ."\n\r"; } } return $output; } } /** * Form builder for the signup broadcast form. * * @param $node * The node that the broadcast form is being attached to. */ function signup_broadcast_form($node) { // Seems lame we need this here, but apparently, we do. :( drupal_set_title(check_plain($node->title)); $addresses = signup_get_email_addresses($node->nid); if (empty($addresses)) { $form['no_users'] = array( '#value' => t('No users have signup up for this @node_type.', array('@node_type' => $node->type)), ); return $form; } $tokens = array('%event', '%username', '%useremail'); if (function_exists('signup_extra_tokens')) { $tokens = array_merge($tokens, signup_extra_tokens()); } sort($tokens); $token_text = t('Supported string substitutions: %tokens.', array('%tokens' => implode(', ', $tokens))); $form['subject'] = array( '#type' => 'textfield', '#title' => t('Subject'), '#required' => true, ); $form['message'] = array( '#type' => 'textarea', '#title' => t('Message body'), '#required' => true, '#description' => t('Body of the email message you wish to send to all users who have signed up for this @node_type.', array('@node_type' => $node->type)) .' '. $token_text, '#rows' => 10, ); $form['send'] = array( '#type' => 'submit', '#value' => t('Send'), ); $form['nid'] = array( '#type' => 'value', '#value' => $node->nid, ); global $user; if (user_access('administer site configuration')) { $form['from'] = array( '#type' => 'textfield', '#title' => t('From'), '#required' => true, '#default_value' => $user->mail, '#weight' => '-10', ); } else { $form['from'] = array( '#value' => t('This message will be sent from: %from', array('%from' => $user->mail)), '#pre' => '', '#post' => '', ); } return $form; } /** * Retrieve a list of all users who have signed up for an event * * @param $nid * @return An array of objects containing signup data */ function signup_get_email_addresses($nid) { $signups = db_query("SELECT u.uid, u.name, u.mail, s_l.anon_mail FROM {signup_log} s_l INNER JOIN {users} u ON u.uid = s_l.uid WHERE s_l.nid = %d", $nid); while ($signup_entry = db_fetch_object($signups)) { $signup_data[] = $signup_entry; } return $signup_data; } /** * Send an email message to all those signed up to an event. * * @param $form_id * @param $form_values */ function signup_broadcast_form_submit($form_id, $form_values) { $addresses = signup_get_email_addresses($form_values['nid']); if (is_array($addresses)) { $from = $form_values['from']; $subject = $form_values['subject']; $event = node_load($form_values['nid']); foreach ($addresses as $signup) { $mail_address = $signup->anon_mail ? $signup->anon_mail : $signup->mail; $trans = array( '%event' => $event->title, '%username' => $signup->uid ? $signup->name : variable_get('anonymous', t('Anonymous')), '%useremail' => $mail_address, // '%info' => $signup_info, ); $trans['%time'] = signup_format_date($event); $message = strtr($form_values['message'], $trans); drupal_mail('signup_broadcast_mail', $mail_address, $subject, $message, $from); watchdog('signup', t('Broadcast email for %event sent to %email.', array('%event' => $event->title, '%email' => $mail_address)), WATCHDOG_NOTICE, l(t('view'), 'node/'. $event->nid)); } } drupal_set_message(t('Message sent to all users who have signed up')); } /** * @defgroup signup_views Views-integration hooks */ /** * Implementation of hook_views_tables. * @ingroup signup_views * @see _signup_views_tables() */ function signup_views_tables() { require_once(drupal_get_path('module', 'signup') .'/signup_views.inc'); return _signup_views_tables(); } /** * Implementation of hook_views_arguments. * @ingroup signup_views * @see _signup_views_arguments() */ function signup_views_arguments() { require_once(drupal_get_path('module', 'signup') .'/signup_views.inc'); return _signup_views_arguments(); } /** * Implementation of hook_views_default_views. * @ingroup signup_views * @see _signup_views_default_views() */ function signup_views_default_views() { require_once(drupal_get_path('module', 'signup') .'/signup_views.inc'); return _signup_views_default_views(); }