array( 'file' => 'admin.inc', 'path' => $path, 'arguments' => array( 'filter_status_form' => NULL, 'signup_admin_form' => NULL, ), ), 'signup_filter_status_form' => array( 'file' => 'admin.inc', 'path' => $path, 'arguments' => array( 'form' => NULL, ), ), 'signup_admin_form' => array( 'file' => 'admin.inc', 'path' => $path, 'arguments' => array( 'form' => NULL, ), ), 'signup_email_token_custom_data' => array( 'file' => 'email.inc', 'path' => $path, 'arguments' => array( 'signup_data' => NULL, ), ), 'signup_custom_data_email' => array( 'file' => 'email.inc', 'path' => $path, 'arguments' => array( 'data' => NULL, ), ), 'signup_broadcast_sender_copy' => array( 'file' => 'email.inc', 'path' => $path, 'arguments' => array( 'raw_message' => NULL, 'cooked_message' => NULL, ), ), 'signup_user_list' => array( 'file' => 'no_views.inc', 'path' => $path, 'arguments' => array( 'node' => NULL, 'registered_signups' => NULL, 'anon_signups' => NULL, ), ), 'signup_user_schedule' => array( 'file' => 'no_views.inc', 'path' => $path, 'arguments' => array( 'node' => NULL, ), ), 'signup_node_admin_page' => array( 'file' => 'node.admin.inc', 'path' => $path, 'arguments' => array( 'node' => NULL, 'signup_node_admin_summary_form' => NULL, 'signup_node_admin_details_form' => NULL, 'signup_form' => NULL, ), ), 'signup_node_admin_summary_form' => array( 'file' => 'node.admin.inc', 'path' => $path, 'arguments' => array( 'form' => NULL, ), ), 'signup_node_admin_details_form' => array( 'file' => 'node.admin.inc', 'path' => $path, 'arguments' => array( 'form' => NULL, ), ), 'signup_custom_data' => array( 'file' => 'node.admin.inc', 'path' => $path, 'arguments' => array( 'data' => NULL, ), ), 'signup_signups_closed' => array( 'file' => 'node.inc', 'path' => $path, 'arguments' => array( 'node' => NULL, 'current_signup' => '', ), ), 'signup_anonymous_user_login_text' => array( 'file' => 'node.inc', 'path' => $path, 'arguments' => array( 'anon_login_text' => NULL, ), ), 'signup_node_output_header' => array( 'file' => 'node.inc', 'path' => $path, 'arguments' => array( 'node' => NULL, ), ), 'signup_current_signup' => array( 'file' => 'node.inc', 'path' => $path, 'arguments' => array( 'signup_data' => NULL, 'cancel_signup_form' => '', ), ), 'signup_custom_data_table' => array( 'file' => 'node.inc', 'path' => $path, 'arguments' => array( 'data' => NULL, ), ), 'signup_custom_data_rows' => array( 'file' => 'node.inc', 'path' => $path, 'arguments' => array( 'data' => NULL, ), ), 'signup_user_form' => array( 'file' => 'signup_form.inc', 'path' => $path, 'arguments' => array( 'node' => NULL, ), ), 'signup_email_token_anonymous_username' => array( 'file' => 'signup_form.inc', 'path' => $path, 'arguments' => array( 'form_data' => NULL, 'email' => NULL, ), ), 'signup_settings_view_label' => array( 'file' => 'admin.settings.inc', 'path' => drupal_get_path('module', 'signup') .'/includes', 'arguments' => array( 'view' => NULL, ), ), 'signup_token_help' => array( 'file' => 'token_help.inc', 'path' => drupal_get_path('module', 'signup') .'/includes', 'arguments' => array( 'tokens' => NULL, ), ), ); } /** * Implementation of hook_init(). */ function signup_init() { _signup_initialize_scheduler_backend(); // Conditionally load either the views support, or the code that // only should happen if views is not enabled. if (module_exists('views')) { module_load_include('inc', 'signup', 'views/views'); } } /** * Implementation of hook_cron(). * * There are two cron-based tasks that can be performed by the signup module: * sending reminder emails to nodes that will begin soon, and auto-closing * signup on nodes that have already started (depending on settings). Each of * these tasks is rather complicated and depends on the specific date-based * backend module that's currently installed (if any), so each one is handled * in a separate helper function. * * @ingroup signup_core * @see _signup_cron_send_reminders() * @see _signup_cron_autoclose() */ function signup_cron() { module_load_include('inc', 'signup', 'includes/cron'); _signup_initialize_scheduler_backend(); _signup_cron_send_reminders(); _signup_cron_autoclose(); } /** * 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, $type_sql) { // Combine type-specific sql with common_sql. $full_sql = array_merge_recursive($common_sql, $type_sql); $sql = 'SELECT '. implode(', ', $full_sql['fields']); $sql .= ' FROM '. $common_sql['primary'] .' '; if (!empty($full_sql['joins'])) { $sql .= implode(' ', $full_sql['joins']); } if (!empty($full_sql['where'])) { $sql .= ' WHERE ('. implode(') AND (', $full_sql['where']) .')'; } if (!empty($full_sql['group_by'])) { $sql .= ' GROUP BY '. implode(', ', $full_sql['group_by']); } return $sql; } /** * Implementation of hook_help(). * * @ingroup signup_core */ function signup_help($path, $arg) { switch ($path) { 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) or nodes that have a date field (with date.module) 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 signups 1 hour before their start (general setting). Settings exist for resticting signups to selected roles and content types.', array('@event_url' => url('http://drupal.org/project/event'), '@date_url' => url('http://drupal.org/project/date'))) .'
'. 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 (($node = menu_get_object()) && arg(2) == 'signup-broadcast') { 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_get_types('name', $node->type))) .'
'; } // See if we need to add our extra checking and validation while configuring // CCK node types for signup. We only want to do this if the $path // doesn't contain 'help#' since hook_help() is invoked twice on admin // pages. if (!empty($arg[0]) && $arg[0] == 'admin' && $arg[1] == 'content' && function_exists('signup_date_check_node_types') ) { if ($arg[2] == 'types' && $arg[3] != 'add') { signup_date_check_node_types(); } elseif ($arg[2] == 'node-type') { signup_date_check_node_types($arg[3]); } } } /** * Implementation of hook_menu(). * * @ingroup signup_core */ function signup_menu() { $path = drupal_get_path('module', 'signup') .'/includes'; $items = array(); $items['admin/settings/signup'] = array( 'description' => 'Configure settings for signups.', 'access arguments' => array('administer all signups'), 'page callback' => 'drupal_get_form', 'page arguments' => array('signup_settings_form'), 'title' => user_access('access administration pages') ? 'Signup' : 'Signup settings', 'file' => 'admin.settings.inc', 'file path' => $path, ); $items['admin/content/signup'] = array( 'description' => 'View all signup-enabled posts, and open or close signups on them.', 'access arguments' => array('administer all signups'), 'page callback' => 'signup_admin_page', 'title' => 'Signup administration', 'file' => 'admin.signup_administration.inc', 'file path' => $path, ); // Conditionally add any available signup-related tabs to nodes. $items['node/%node/signup'] = array( 'title' => 'Sign up', 'page callback' => 'signup_node_tab', 'page arguments' => array(1), 'access callback' => '_signup_menu_access', 'access arguments' => array(1, 'node'), 'type' => MENU_LOCAL_TASK, 'weight' => 19, 'file' => 'node_output.inc', 'file path' => $path, ); $items['node/%node/signups'] = array( 'title' => 'Signups', 'page callback' => 'signup_node_admin_page', 'page arguments' => array(1), 'access callback' => '_signup_menu_access', 'access arguments' => array(1, 'admin'), 'type' => MENU_LOCAL_TASK, 'weight' => 20, 'file' => 'node_admin.inc', 'file path' => $path, ); $items['node/%node/signups/confirm'] = array( 'page callback' => 'drupal_get_form', 'page arguments' => array('signup_cancel_multiple_confirm', 1), 'access callback' => '_signup_menu_access', 'access arguments' => array(1, 'admin'), 'type' => MENU_CALLBACK, 'file' => 'node_admin.inc', 'file path' => $path, ); $items['node/%node/signup-broadcast'] = array( 'title' => 'Signup broadcast', 'page callback' => 'drupal_get_form', 'page arguments' => array('signup_broadcast_form', 1), 'access callback' => '_signup_menu_access', 'access arguments' => array(1, 'email'), 'type' => MENU_LOCAL_TASK, 'weight' => 21, 'file' => 'broadcast.inc', 'file path' => $path, ); // Add extra menu items if we're not using views. if (!module_exists('views')) { module_load_include('inc', 'signup', 'includes/no_views'); signup_no_views_menu($items); } return $items; } /** * Determine menu access for a given type of signup menu item. * * This ensures that the node is signup enabled, and that that the current * user should have permission to view the requested menu item type. * * @param $node * The fully loaded node object from the menu autoloader. * @param $menu_type * String specifying what kind of menu item to test access for. Can be * 'node' for the tab holding the signup form and attendee listing, 'admin' * for the signup administration tab, or 'email' for the broadcast tab. * * @return * TRUE if the current node is signup enabled and the current user has * permisison to access to requested menu item, otherwise FALSE. * * @see signup_menu() */ function _signup_menu_access($node, $menu_type = 'node') { global $user; // If the node isn't signup enabled, immediately return failure. if (empty($node->signup)) { return FALSE; } switch ($menu_type) { case 'node': // See if this node is configured to display the signup form and // attendee list on a separate tab, and if the node has signup output. return (variable_get('signup_form_location', 'node') == 'tab') && _signup_needs_output($node); case 'email': $email_all = user_access('email all signed up users'); $email_own = user_access('email users signed up for own content') && ($user->uid == $node->uid); return $email_all || $email_own; case 'admin': $admin_all = user_access('administer all signups'); $admin_own = user_access('administer signups for own content') && ($user->uid == $node->uid); return $admin_all || $admin_own; } } function _signup_user_menu_access($account) { global $user; return user_access('administer all signups') || $account->uid == $user->uid; } /** * Initialize the necessary scheduler backend(s). */ function _signup_initialize_scheduler_backend() { module_load_include('inc', 'signup', '/includes/scheduler'); _signup_load_scheduler_includes(); } /** * 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_user(). * * When a user is deleted, cancel all of that user's signups to remove all * instances of that user from the {signup_log} table, free up space in nodes * with signup limits, etc. * * @ingroup signup_core */ function signup_user($type, &$edit, &$user, $category = NULL) { switch ($type) { case 'delete': $uids = array(); if (is_array($edit['accounts'])) { // A multi-user delete from Admin > User management > Users. $uids = $edit['accounts']; } else { // A single-user delete from the edit tab on the user's profile. $uids[] = $edit['uid']; } foreach ($uids as $uid) { $nids = db_query("SELECT nid FROM {signup_log} WHERE uid = %d", $uid); while ($data = db_fetch_object($nids)) { signup_cancel_signup($uid, $data->nid); } } break; } // If we're not using views, we need to support additional user // operations, for example, to add the user's current signup // schedule to their user profile page. if (!module_exists('views')) { module_load_include('inc', 'signup', 'includes/no_views'); return _signup_user_no_views($type, $edit, $user, $category); } } /** * Implementation of hook_form_alter(). * * @ingroup signup_core */ function signup_form_alter(&$form, &$form_state, $form_id) { if (!empty($form['type']['#value'])) { if ($form_id == $form['type']['#value'] .'_node_form') { module_load_include('inc', 'signup', 'includes/node_form'); signup_alter_node_form($form, $form_state, $form_id); } } } /** * Alters the form for administrator settings per node type. * (admin/content/types) */ function signup_form_node_type_form_alter(&$form, &$form_state) { $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'))), ); if (!empty($type) && function_exists('_signup_date_alter_node_type_form')) { _signup_date_alter_node_type_form($form, $form_state); } } /** * @defgroup signup_nodeapi Functions for nodeapi integration */ /** * Implementation of hook_nodeapi(). * * @ingroup signup_nodeapi */ function signup_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { switch ($op) { case 'insert': case 'update': module_load_include('inc', 'signup', 'includes/node_form'); signup_save_node($node, $op); 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)); $signup = db_fetch_object($result); // Load signup data for both new nodes w/ enabled node types, // and any existing nodes that are already signup enabled. if ((!$node->nid && variable_get('signup_node_default_state_'. $node->type, 'disabled') == 'enabled_on') || ($node->nid && !empty($signup))) { $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') { module_load_include('inc', 'signup', 'includes/node_output'); $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; } } /** * 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; } /** * 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' AND nid = %d", $anon_mail, $nid); } 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', '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', 'Signups reopened for %title.', array('%title' => $node->title), WATCHDOG_NOTICE, l(t('view'), 'node/'. $nid)); } } /** * Returns a list of content types that have signups enabled */ function signup_content_types() { $signup_content_types = array(); foreach (node_get_types('names') as $content_type => $content_name) { if (variable_get('signup_node_default_state_'. $content_type, 'disabled') != 'disabled') { $signup_content_types[] = $content_type; } } return $signup_content_types; } /** * 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. $signup_anon_mail = ''; if (!empty($signup_form['signup_anon_mail'])) { $signup_anon_mail = $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_result(db_query("SELECT COUNT(*) FROM {signup_log} WHERE anon_mail = '%s' AND nid = %d", $signup_anon_mail, $node->nid))) { drupal_set_message(t('Anonymous user %email is already signed up for %title', array('%email' => $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. $result = 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']); $user = db_fetch_object($result); if (!empty($user)) { 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, $user); $signup_info = array(); if (!empty($signup_form['signup_form_data'])) { $signup_info = $signup_form['signup_form_data']; } if (!empty($extra)) { $signup_info = array_merge($signup_info, $extra); } $signup_form_data = serialize($signup_info); // 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 information about this %node_type.', array('%node_type' => node_get_types('name', $node->type))) : ''; $reminder_email = $node->signup_send_reminder ? ' '. t('You will receive a reminder email !number !days before the %node_type.', array('!number' => $node->signup_reminder_days_before, '!days' => format_plural($node->signup_reminder_days_before, 'day', 'days'), '%node_type' => node_get_types('name', $node->type))) : ''; // 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_anon_mail, $curtime, $signup_form_data); $from = variable_get('site_mail', ini_get('sendmail_from')); // Get the hard-coded tokens provided by the signup module to use // for the confirmation and/or forwarding emails. We need to create // an object representing the user's signup to get the right values. $signup = $user; $signup->form_data = $signup_info; if (!empty($signup_anon_mail)) { $signup->anon_mail = $signup_anon_mail; } $user_mail = _signup_get_email($signup); $node_type_name = node_get_types('name', $node->type); // If a confirmation is to be sent, compose the mail message, // replace the tokens with the right values, and send it. if ($node->signup_send_confirmation && $user_mail) { $params = array( 'subject' => t('Signup confirmation for !node_type: !title', array('!node_type' => $node_type_name, '!title' => $node->title)), 'body' => $node->signup_confirmation_email, 'node' => $node, 'signup' => $signup, ); if (module_exists('token')) { $params['body'] = token_replace($params['body'], 'node', $node); } $language = user_preferred_language($signup); drupal_mail('signup', 'signup_confirmation_mail', $user_mail, $language, $params, $from); } // If a forwarding email is to be sent, compose the mail message, // replace the tokens with the right values, and send it. if ($node->signup_forwarding_email) { $message = t('The following information was submitted as a signup for !title', array('!title' => $node->title)); if (_signup_get_node_scheduler($node) != 'none') { $message .= "\n\r". t('Date/Time: !time', array('!time' => signup_format_date($node))); } $message .= "\n\r\n\r". t('Username: !name', array('!name' => empty($user->uid) ? variable_get('anonymous', t('Anonymous')) : $user->name)); if (!empty($user->uid)) { // For authenticated users, just include a link to their profile page. $message .= "\n\r". t('Profile page: !url', array('!url' => url('user/'. $user->uid, array('absolute' => TRUE)))); } else { // For anonymous users, their email is all we've got, so disclose it. $message .= "\n\r". t('E-mail: !email', array('!email' => $user_mail)); } if (!empty($signup_tokens['%user_signup_info'])) { $message .= "\n\r\n\r". $signup_tokens['%user_signup_info']; } $params = array( 'subject' => t('Signup confirmation for !node_type: !title', array('!node_type' => $node_type_name, '!title' => $node->title)), 'body' => $message, 'node' => $node, 'signup' => $signup, 'header' => array('From' => t('New !node_type Signup', array('!node_type' => $node_type_name)) ."<$from>"), ); if (module_exists('token')) { $params['body'] = token_replace($params['body'], 'node', $node); } $language = user_preferred_language($signup); drupal_mail('signup', 'signup_forwarding_mail', $node->signup_forwarding_email, $language, $params, $from); } 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(); } } /** * 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))); } elseif ($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; } } elseif (($node->signup_total < $limit) && (!$node->signup_status) && (!_signup_node_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))); } } elseif ($type == 'limit') { drupal_set_message(t('Signup limit updated for !title.', array('!title' => $node_link))); } } elseif ($type == 'limit') { // These checks should only happen if the limit was just changed... if (!$node->signup_status && !_signup_node_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; } /** * Converts an arbitrary string into something safe to use for a CSS id. * * Stolen wholesale from the Zen theme. ;) * @see zen_id_safe() */ function signup_id_safe($string) { // Replace with dashes anything that isn't a-zA-Z, numbers, dashes, or // underscores. $string = drupal_strtolower(preg_replace('/[^a-zA-Z0-9_-]+/', '-', $string)); // If the first character is not a-z, add 'id' in front. // Don't use ctype_alpha since it's locale aware. if (!ctype_lower($string{0})) { $string = 'id'. $string; } return $string; } /** * Implementation of hook_views_api(). */ function signup_views_api() { return array( 'api' => 2.0, 'path' => drupal_get_path('module', 'signup') . '/views', ); } /** * Implementation of hook_mail(). * * Constructs all of the email messages generated by the signup module. * * @param $key * Unique key to indicate what message to build. * @param $message * Reference to the message array being built. * @param $params * Array of parameters to indicate what text to include in the message body. * If $params['ignore_tokens'] is TRUE, none of the signup-provided tokens * in the message body will be replaced, otherwise, tokens are replaced * using the node passed in as $params['node'] and the signup data from * $params['signup']. * * @see drupal_mail() * @see _signup_cron_send_reminders() * @see signup_sign_up_user() * @see signup_broadcast_form_submit() */ function signup_mail($key, &$message, $params) { if (empty($params['ignore_tokens'])) { $tokens = _signup_get_email_tokens($params['node'], $params['signup']); $body = strtr($params['body'], $tokens); $subject = strtr($params['subject'], $tokens); } else { $body = $params['body']; $subject = $params['subject']; } $message['subject'] .= str_replace(array("\r", "\n"), '', $subject); $message['body'][] = drupal_html_to_text($body); } /** * Helper function that returns an array of tokens for signup emails. * * These are the hard-coded tokens provided by signup.module which are * available even when token.module is not enabled. * * @param $node * An object with info about the signup-enabled node. Must contain * at least 'nid', 'title', and 'TODO: time?' fields. * @param $signup * An object with info about the user who signed up. * * @return * An array of tokens and their replacement values. */ function _signup_get_email_tokens($node, $signup) { // Tokens about the node itself are easy and don't require any logic. $tokens = array( '%node_title' => $node->title, '%node_url' => url('node/'. $node->nid, array('absolute' => TRUE)), '%node_start_time' => signup_format_date($node), ); // Tokens about the user are harder, since we need to potentially do // things differently for anonymous vs. authenticated signups, and // might want to do other processing on the signup form data. // Get the array of custom signup data (if any). $signup_data = array(); if (!empty($signup->form_data)) { if (is_array($signup->form_data)) { $signup_data = $signup->form_data; } else { $signup_data = unserialize($signup->form_data); } $tokens['%user_signup_info'] = theme('signup_email_token_custom_data', $signup_data); } // Determine if this is an anon signup or not, and get the right info. if (!empty($signup->anon_mail)) { $tokens['%user_name'] = theme('signup_email_token_anonymous_username', $signup_data, $signup->anon_mail); } else { $tokens['%user_name'] = $signup->name; } $tokens['%user_mail'] = _signup_get_email($signup); return $tokens; } /** * Get the email address to use for a given signup. * * @param $signup * An object containing signup data, in particular 'mail' and 'anon_mail'. * * @return * The appropriate email address to use to contact the signed up user. */ function _signup_get_email($signup) { return !empty($signup->anon_mail) ? $signup->anon_mail : $signup->mail; } /** * Implementation of hook_panels_include_directory(). * * @param $plugin_type * The plugin type for which the Panels engine is currently requesting the * location of an include directory. * * @return * The location of the include directory for plugin type being requested, * relative to the base directory of the module implementing this hook. */ function signup_panels_include_directory($plugin_type) { if ($plugin_type == 'content_types') { return 'panels/'. $plugin_type; } }