'image_button', '#title' => t('My button'), '#return_value' => 'my_data', '#src' => 'my/image/path.jpg', ); */ /* TODO hook_user('view') The return value of hook_user('view') has changed, to match the process that nodes use for rendering. Modules should add their custom HTML to $account->content element. Further, this HTML should be in the form that drupal_render() recognizes. */ /* TODO New user_mail_tokens() method may be useful. user.module now provides a user_mail_tokens() function to return an array of the tokens available for the email notification messages it sends when accounts are created, activated, blocked, etc. Contributed modules that wish to make use of the same tokens for their own needs are encouraged to use this function. */ /* TODO There is a new hook_watchdog in core. This means that contributed modules can implement hook_watchdog to log Drupal events to custom destinations. Two core modules are included, dblog.module (formerly known as watchdog.module), and syslog.module. Other modules in contrib include an emaillog.module, included in the logging_alerts module. See syslog or emaillog for an example on how to implement hook_watchdog. function example_watchdog($log = array()) { if ($log['severity'] == WATCHDOG_ALERT) { mysms_send($log['user']->uid, $log['type'], $log['message'], $log['variables'], $log['severity'], $log['referer'], $log['ip'], format_date($log['timestamp'])); } } */ /* TODO Implement the hook_theme registry. Combine all theme registry entries into one hook_theme function in each corresponding module file. function roundcube_theme() { return array( 'roundcube_iframe' => array( 'file' => 'roundcube.module', 'arguments' => array( 'src' => NULL, 'width' => NULL, 'height' => NULL, ), ), ); } */ // TODO: roundcube_variable_passphrase() should get its value from // settings.php or .htaccess or vhost. /** * @file * RoundCube - a Drupal module that integrates RoundCube Webmail 0.2 * with Drupal. * * Authors: * Thomas Barregren . * Joakim Stai . * * Sponsors: * Norrlandsflyg * Webbredaktören */ /***************************************************************************** * PERMISSION HOOK *****************************************************************************/ /** * Implementation of hook_perm(). */ function roundcube_perm() { return array('access webmail'); } /***************************************************************************** * MENU HOOK *****************************************************************************/ /** * Implementation of hook_menu(). */ function roundcube_init() { _roundcube_thickbox(); } function roundcube_menu() { $items = array(); /* TODO Non menu code that was placed in hook_menu under the '!$may_cache' block so that it could be run during initialization, should now be moved to hook_init. Previously we called hook_init twice, once early in the bootstrap process, second just after the bootstrap has finished. The first instance is now called boot instead of init. In Drupal 6, there are now two hooks that can be used by modules to execute code at the beginning of a page request. hook_boot() replaces hook_boot() in Drupal 5 and runs on each page request, even for cached pages. hook_boot() now only runs for non-cached pages and thus can be used for code that was previously placed in hook_menu() with $may_cache = FALSE: Dynamic menu items under a '!$may_cache' block can often be simplified to remove references to arg(n) and use of '%' to check conditions. See http://drupal.org/node/103114. The title and description arguments should not have strings wrapped in t(), because translation of these happen in a later stage in the menu system. if ($may_cache) { $items['webmail'] = _roundcube_menu_roundcube_view(); $items[] = _roundcube_menu_roundcube_thickbox(); $items['admin/settings/roundcube'] = _roundcube_menu_roundcube_settings(); } else { _roundcube_thickbox(); $items[] = _roundcube_menu_roundcube_callback(); } */ $items['admin/settings/roundcube'] = array( 'title' => 'RoundCube', 'page callback' => 'drupal_get_form', 'page arguments' => array('roundcube_admin_settings'), 'access arguments' => array('administer webmail'), 'type' => MENU_NORMAL_ITEM ); $items['webmail/credentials'] = array( 'page callback' => 'roundcube_callback', 'page arguments' => array(arg(2)), 'access arguments' => array('access webmail'), 'type' => MENU_CALLBACK ); $items['webmail/thickbox'] = array( 'type' => MENU_CALLBACK, 'page callback' => 'roundcube_thickbox', 'access arguments' => array('access webmail') ); $items['webmail'] = array( 'title' => 'Webmail', 'type' => MENU_NORMAL_ITEM, 'page callback' => 'roundcube_login', 'access arguments' => array('access webmail') ); return $items; } function _roundcube_menu_roundcube_view() { return array( 'path' => "webmail", 'title' => t('Webmail'), 'type' => MENU_NORMAL_ITEM, 'callback' => 'roundcube_login', 'access' => user_access('access webmail'), ); } function _roundcube_menu_roundcube_thickbox() { return array( 'path' => "webmail/thickbox", 'type' => MENU_CALLBACK, 'callback' => 'roundcube_thickbox', 'access' => user_access('access webmail'), ); } function _roundcube_menu_roundcube_callback() { return array( 'path' => "webmail/credentials", 'type' => MENU_CALLBACK, 'callback' => 'roundcube_callback', 'callback arguments' => array(arg(2)), 'access' => true, ); } function _roundcube_menu_roundcube_settings() { return array( //'path' => "admin/settings/roundcube", 'title' => t('RoundCube'), 'description' => t('Configure the integration of RoundCube.'), 'type' => MENU_NORMAL_ITEM, 'callback' => 'drupal_get_form', 'callback arguments' => array('roundcube_admin_settings'), 'access' => user_access('administer webmail'), ); } /***************************************************************************** * ADMINISTRATION PAGES *****************************************************************************/ function roundcube_admin_settings() { $form['roundcube_url'] = array( '#type' => 'textfield', '#title' => t('URL to RoundCube'), '#description' => t('An absolute URL to the index.php file of your RoundCube installation (e.g. https://www.example.com/webmail/index.php).'), '#default_value' => _roundcube_variable('roundcube_url', null, ''), '#required' => true, '#size' => 40, '#maxlength' => 255, ); $form['roundcube_timeout'] = array( '#type' => 'textfield', '#title' => t('Timeout'), '#description' => t('The number of seconds before automatically logging out from RoundCube.'), '#default_value' => _roundcube_variable('roundcube_timeout', null, 60), '#required' => true, '#size' => 40, '#maxlength' => 255, ); $form['presentation'] = array( '#type' => 'fieldset', '#title' => t('Presentation'), '#collapsible' => true, '#collapsed' => false, ); if (module_exists('thickbox')) { $form['presentation']['roundcube_presentation'] = array( '#type' => 'radios', '#title' => t('Presentation'), '#description' => t('Select whether to display RoundCube in an IFrame or in a Thickbox.'), '#options' => array('iframe' => t('IFrame'), 'thickbox' => t('Thickbox')), '#default_value' => _roundcube_variable('roundcube_presentation', null, 'iframe'), ); } $form['presentation']['roundcube_width'] = array( '#type' => 'textfield', '#title' => t('Width'), '#description' => t('The width of RoundCube in pixels (e.g. 700) or percentage (e.g. 100%). A width of at least 700 is recommended.'), '#default_value' => _roundcube_variable('roundcube_width', null, '100%'), '#required' => true, '#size' => 40, '#maxlength' => 6, ); $form['presentation']['roundcube_height'] = array( '#type' => 'textfield', '#title' => t('Height'), '#description' => t('The height of RoundCube in pixels (e.g. 700) or percentage (e.g. 100%).'), '#default_value' => _roundcube_variable('roundcube_height', null, '600'), '#required' => true, '#size' => 40, '#maxlength' => 6, ); if (module_exists('thickbox')) { $form['presentation']['roundcube_width']['#description'] .= ' '. t('Thickbox does not support percentages.'); $form['presentation']['roundcube_height']['#description'] .= ' '. t('Thickbox does not support percentages.'); } return system_settings_form($form); } /***************************************************************************** * USER HOOK *****************************************************************************/ // TODO: There should be a setting that allows the administrator to choose // wehther the Drupal passwords shopuld be used or if a separate password // should be given. Should the password be stored in the table in the former // case. It violates DRY. /** * Implementation of hook_user(). */ function roundcube_user($op, &$edit, &$account, $category = null) { static $accepted = array('insert', 'update', 'delete'); $args = array(&$edit, &$account, $category); return _roundcube_dispatcher(__FUNCTION__, $op, $args, $accepted); } function _roundcube_user_insert(&$edit, &$account) { roundcube_password_insert($account->uid, $edit['pass']); } function _roundcube_user_update(&$edit, &$account) { if ($edit['pass']) { _roundcube_user_delete($edit, $account); _roundcube_user_insert($edit, $account); } } function _roundcube_user_delete(&$edit, &$account) { roundcube_password_delete($account->uid); } /***************************************************************************** * FOOTER HOOK *****************************************************************************/ /** * Implementation of hook_footer(). */ function roundcube_footer() { return roundcube_logout(); } /***************************************************************************** * PASSWORD *****************************************************************************/ function roundcube_password_get($uid) { return _roundcube_db_get_password($uid); } function roundcube_password_insert($uid, $pass) { _roundcube_db_insert_password($uid, $pass); } function roundcube_password_delete($uid) { return _roundcube_db_delete_password($uid, $pass); } /***************************************************************************** * ROUNDCUBE INTEGRATION *****************************************************************************/ function roundcube_login() { global $user; // Append the user's email address to the title. drupal_set_title(drupal_get_title() .' — '. $user->mail); // TODO: themable? // Get URL for logging into and displaying RoundCube Webmail. $url = _roundcube_login_url(); $width = _roundcube_variable('roundcube_width', null, '100%'); $height = _roundcube_variable('roundcube_height', null, '600'); if (_roundcube_variable('roundcube_url', null, '') == '') { // Display a warning if a URL to RoundCube has not been specified. drupal_set_message(t('You must specify a URL to RoundCube in the administration page for the module.', array('!url' => url('admin/settings/roundcube'))), 'error'); $output = ''; } else { // If we have a URL, embed RoundCube Webmail within the page. $output = theme('roundcube_iframe', $url, $width, $height); } return $output; } function _roundcube_login_url() { global $user; // Keep track of login status so we can logout when the user leaves the page. $_SESSION['roundcube_login'] = true; $key = roundcube_key_create($user->uid); $url = _roundcube_variable('roundcube_url', null, ''); // Return a URL with the key that RoundCube Webmail can use to call back and // get the e-mail and clear text password of user. return $url ."?drupal=". url("webmail/credentials/$key", array('query' => "&_user=$user->mail", 'fragment' => null, 'absolute' => TRUE)); } function roundcube_logout() { if ($step = $_SESSION['roundcube_login']) { if ($step !== 2) { $_SESSION['roundcube_login'] = 2; } else { $_SESSION['roundcube_login'] = false; $url = _roundcube_variable('roundcube_url', null, '') .'?_action=logout'; return theme('roundcube_iframe', $url, 0, 0); } } } function roundcube_callback(&$form_state, $key) { if ($uid = roundcube_key_validate($key)) { print serialize(roundcube_password_get($uid)); } exit; } function roundcube_key_create($user) { $expires = time() + _roundcube_variable('roundcube_timeout', null, 60); $key = md5($user->mail . $user->pass . $expires); _roundcube_db_insert_credential($key, $user, $expires); return $key; } function roundcube_key_validate($form, &$form_state) { $credential = _roundcube_db_get_credential($key); _roundcube_db_delete_credential($key); if ($credential && time() < $credential->expires) { return $credential->uid; } } /***************************************************************************** * THICKBOX INTEGRATION *****************************************************************************/ function roundcube_thickbox() { // Redirect to RoundCube Webmail, logging the user in. $url = _roundcube_login_url(); header('Location: '. $url, TRUE, 307); exit; } function _roundcube_thickbox() { $presentation = _roundcube_variable('roundcube_presentation', null, 'iframe'); $url = _roundcube_variable('roundcube_url', null, ''); // Attach Thickbox to menu item link if Thickbox exists and is the // selected display, and a URL to RoundCube has been specified. if ($presentation == 'thickbox' && module_exists('thickbox') && !empty($url)) { $width = _roundcube_variable('roundcube_width', null, '700'); $height = _roundcube_variable('roundcube_height', null, '500'); $url = url('webmail/thickbox', array('query' => "TB_iframe=true&width=$width&height=$height", 'fragment' => null, 'absolute' => TRUE)); drupal_add_js(array('RoundCube' => array('url' => $url)), 'setting', 'header', false, false); drupal_add_js(drupal_get_path('module', 'roundcube') .'/roundcube.thickbox.js'); } } /***************************************************************************** * DATABASE *****************************************************************************/ function _roundcube_db_get_password($uid) { $key = roundcube_db_crypto_key(); if ($db_result = _roundcube_db_query(__FUNCTION__, $key, $uid)) { if ($object = db_fetch_object($db_result)) { return $object->password; } } } function _roundcube_db_insert_password($uid, $pass) { $key = roundcube_db_crypto_key(); return (boolean) _roundcube_db_query(__FUNCTION__, $uid, $pass, $key); } function _roundcube_db_update_password($uid, $pass) { $key = roundcube_db_crypto_key(); return (boolean) _roundcube_db_query(__FUNCTION__, $pass, $key, $uid); } function _roundcube_db_delete_password($uid) { $key = roundcube_db_crypto_key(); return (boolean) _roundcube_db_query(__FUNCTION__, $uid, $key); } function _roundcube_db_insert_credential($key, $user, $expires) { return (boolean) _roundcube_db_query(__FUNCTION__, $key, $user, $expires); } function _roundcube_db_get_credential($key) { if ($db_result = _roundcube_db_query(__FUNCTION__, $key)) { return db_fetch_object($db_result); } } function _roundcube_db_delete_credential($key) { return (boolean) _roundcube_db_query(__FUNCTION__, $key); } function roundcube_db_crypto_key() { return md5(roundcube_variable_passphrase()); } /***************************************************************************** * VARIABLES *****************************************************************************/ function roundcube_variable_passphrase($passphrase = null) { return _roundcube_variable("roundcube_passphrase", $passphrase, 'roundcube_passphrase'); } /***************************************************************************** * HELPERS *****************************************************************************/ /** * Sets and gets the named persisted variable. */ function _roundcube_variable($name, $value = null, $default = null) { if (isset($value)) { variable_set($name, $value); } return variable_get($name, $default); } /** * Dispatch the call of the hook $hook to appropriate handler. */ function _roundcube_dispatcher($hook, $op, $args, &$accepted = null) { if ($accepted !== null && !in_array($op, $accepted)) return; $function = '_'. $hook .'_'. $op; if ($accepted === null && !function_exists($function)) return; return call_user_func_array($function, $args); } /** * Returns the result of the database specific query named $query_name. */ function _roundcube_db_query($query_name) { static $included = false; // Include the database specific file with queries. if (!$included) { global $db_type; $db = $db_type == 'mysqli' ? 'mysql' : $db_type; $db_file = "roundcube.$db.inc"; if (!is_file(drupal_get_path('module', 'roundcube') ."/$db_file")) { $msg = t("roundcube doesn't support $db_type."); drupal_set_message($msg, 'error'); return false; } include_once $db_file; $included = true; } // Get the arguments. $args = func_get_args(); array_shift($args); if (isset($args[0]) and is_array($args[0])) { $args = $args[0]; } // Get the query, and new arguments, if any. $query_function = $query_name .'_query'; if (!function_exists($query_function)) { $msg = t("roundcube can't find the '$query_name' query for $db_type."); drupal_set_message($msg, 'error'); return false; } $query = $query_function($args); // Build the query. $query = db_prefix_tables($query); _db_query_callback($args, true); $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); // Do the database query, and return the status. return _db_query($query); } /***************************************************************************** * THEME FUNCTIONS *****************************************************************************/ function theme_roundcube_iframe($src, $width, $height) { // Append 'px' to width and height if they're not percentages. if (!strpos($width, '%')) $width .= 'px'; if (!strpos($height, '%')) $height .= 'px'; // Be nice to those still living in prehistorical times. $noframes = t('Sorry, your browser does not support frames.'); $output = <<$noframes EOT; return $output; }