email.module 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. <?php
  2. /**
  3. * @file
  4. * Module file for the email module, which creates a email address field.
  5. */
  6. /**
  7. * Implements hook_field_info().
  8. */
  9. function email_field_info() {
  10. return array(
  11. 'email' => array(
  12. 'label' => 'Email',
  13. 'description' => t('This field stores and renders email addresses.'),
  14. 'default_widget' => 'email_textfield',
  15. 'default_formatter' => 'email_default',
  16. 'property_type' => 'text',
  17. ),
  18. );
  19. }
  20. /**
  21. * Implements hook_migrate_api().
  22. */
  23. function email_migrate_api() {
  24. return array(
  25. 'api' => 2,
  26. 'field handlers' => array('MigrateEmailFieldHandler'),
  27. );
  28. }
  29. /**
  30. * Implements hook_field_validate().
  31. *
  32. * Possible error codes:
  33. * - 'email_invalid': The email address is not valid
  34. */
  35. function email_field_validate($obj_type, $object, $field, $instance, $langcode, $items, &$errors) {
  36. foreach ($items as $delta => $item) {
  37. if ($item['email'] != '' && !valid_email_address(trim($item['email']))) {
  38. $message = t('"%mail" is not a valid email address', array('%mail' => $item['email']));
  39. $errors[$field['field_name']][$langcode][$delta][] = array(
  40. 'error' => "email_invalid",
  41. 'message' => $message,
  42. );
  43. }
  44. }
  45. }
  46. /**
  47. * Implements hook_field_widget_error().
  48. */
  49. function email_field_widget_error($element, $error, $form, &$form_state) {
  50. form_error($element, $error['message']);
  51. }
  52. /**
  53. * Implements hook_content_is_empty().
  54. */
  55. function email_field_is_empty($item, $field) {
  56. if (empty($item['email'])) {
  57. return TRUE;
  58. }
  59. return FALSE;
  60. }
  61. /**
  62. * Implements hook_field_formatter_info().
  63. *
  64. */
  65. function email_field_formatter_info() {
  66. $formats = array(
  67. 'email_default' => array(
  68. 'label' => t('Default email link'),
  69. 'description' => t('Display the email address as a mailto link.'),
  70. 'field types' => array('email'),
  71. ),
  72. 'email_contact' => array(
  73. 'label' => t('Email contact form'),
  74. 'description' => t('Display a contact form.'),
  75. 'field types' => array('email'),
  76. ),
  77. 'email_plain' => array(
  78. 'label' => t('Email plain text'),
  79. 'description' => t('Display the email address as plain text.'),
  80. 'field types' => array('email'),
  81. ),
  82. );
  83. if (module_exists('spamspan')) {
  84. $formats += array(
  85. 'email_spamspan' => array(
  86. 'label' => t('Email SpamSpan'),
  87. 'field types' => array('email'),
  88. ),
  89. );
  90. }
  91. return $formats;
  92. }
  93. /**
  94. * Implements hook_field_formatter_view().
  95. */
  96. function email_field_formatter_view($object_type, $object, $field, $instance, $langcode, $items, $display) {
  97. $element = array();
  98. switch ($display['type']) {
  99. case 'email_default':
  100. foreach ($items as $delta => $item) {
  101. $output = l($item['email'], 'mailto:' . $item['email']);
  102. $element[$delta] = array('#markup' => $output);
  103. }
  104. break;
  105. case 'email_contact':
  106. $ids = entity_extract_ids($object_type, $object);
  107. foreach ($items as $delta => $item) {
  108. $element[$delta] = array('#markup' => l(t('Contact person by email'), 'email/' . $object_type . '/' . $ids[0] . '/' . $instance['field_name']));
  109. // Since email is always sent to first item's email, break after any email address found.
  110. break;
  111. }
  112. break;
  113. case 'email_plain':
  114. foreach ($items as $delta => $item) {
  115. $element[$delta] = array('#markup' => check_plain($item['email']));
  116. }
  117. break;
  118. case 'email_spamspan':
  119. foreach ($items as $delta => $item) {
  120. if (module_exists('spamspan')) {
  121. $element[$delta] = array('#markup' => spamspan($item['email']));
  122. }
  123. else {
  124. $output = l($item['email'], 'mailto:' . $item['email']);
  125. $element[$delta] = array('#markup' => $output);
  126. }
  127. }
  128. break;
  129. }
  130. return $element;
  131. }
  132. /**
  133. * Implements hook_field_widget_info().
  134. */
  135. function email_field_widget_info() {
  136. return array(
  137. 'email_textfield' => array(
  138. 'label' => t('Text field'),
  139. 'field types' => array('email'),
  140. 'settings' => array('size' => 60),
  141. ),
  142. );
  143. }
  144. /**
  145. * Implements hook_field_widget_settings_form().
  146. */
  147. function email_field_widget_settings_form($field, $instance) {
  148. $widget = $instance['widget'];
  149. $settings = $widget['settings'];
  150. $form['size'] = array(
  151. '#type' => 'textfield',
  152. '#title' => t('Size of textfield'),
  153. '#default_value' => $settings['size'],
  154. '#required' => TRUE,
  155. '#element_validate' => array('_element_validate_integer_positive'),
  156. );
  157. return $form;
  158. }
  159. /**
  160. * Implements hook_field_widget_form().
  161. */
  162. function email_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $base) {
  163. $element = $base;
  164. $element['email'] = $base + array(
  165. '#type' => 'textfield',
  166. '#default_value' => isset($items[$delta]['email']) ? $items[$delta]['email'] : NULL,
  167. '#size' => $instance['widget']['settings']['size'],
  168. '#prefix' => '<div class="text-full-wrapper">',
  169. '#suffix' => '</div>',
  170. );
  171. return $element;
  172. }
  173. /**
  174. * Implements hook_menu().
  175. */
  176. function email_menu() {
  177. $items['email/%/%/%'] = array(
  178. 'title' => 'Email Contact Form',
  179. 'page callback' => 'email_mail_page',
  180. 'page arguments' => array(1, 2, 3),
  181. 'access callback' => 'user_access',
  182. 'access arguments' => array('access content'),
  183. 'type' => MENU_CALLBACK,
  184. );
  185. $items['admin/config/content/email'] = array(
  186. 'title' => 'Email Contact Form Settings',
  187. 'description' => 'Administer flood control settings for email contact forms',
  188. 'page callback' => 'drupal_get_form',
  189. 'page arguments' => array('email_admin_settings'),
  190. 'access arguments' => array('administer site configuration'),
  191. 'type' => MENU_NORMAL_ITEM,
  192. );
  193. return $items;
  194. }
  195. /**
  196. * Access callback for the email contact page.
  197. *
  198. * Checks whether the current user has view access to the entity. Access checks
  199. * are performed for the fieldable core entity types, including nodes, users,
  200. * comments and taxonomy terms. Furthermore entity types using Entity API's
  201. * access system are supported. For custom entity types that are not using the
  202. * Entity API, at least the access content permission is checked in the menu
  203. * access callback.
  204. *
  205. * This function is called within the email page callback, as it takes care of
  206. * loading the entity itself. If the entity is found, access checks are
  207. * performed with this function.
  208. *
  209. * @param $entity_type
  210. * The entity type
  211. * @param $entity
  212. * The entity for which the access should be checked
  213. * @param $field_info
  214. * The field info for the email field.
  215. *
  216. * @return TRUE if the current user has view access, otherwise FALSE.
  217. */
  218. function email_mail_page_access($entity_type, $entity, $field_info) {
  219. // Check for field access.
  220. if (!field_access('view', $field_info, $entity_type, $entity)) {
  221. return FALSE;
  222. }
  223. // Check the access for fieldable core entities, including nodes, users,
  224. // comments and taxonomy terms.
  225. if ($entity_type == 'node') {
  226. return node_access('view', $entity);
  227. }
  228. elseif ($entity_type == 'user') {
  229. global $user;
  230. if ($entity->uid == $user->uid && $entity->uid) {
  231. return TRUE;
  232. }
  233. if (user_access('administer users') || (user_access('access user profiles') && $entity->status)) {
  234. return TRUE;
  235. }
  236. return FALSE;
  237. }
  238. elseif ($entity_type == 'comment') {
  239. return comment_access('view', $entity);
  240. }
  241. elseif ($entity_type == 'taxonomy_term') {
  242. if (user_access('administer taxonomy') || user_access('access content')) {
  243. return TRUE;
  244. }
  245. return FALSE;
  246. }
  247. // Use Entity API for checking the view access for non-core entity types, if
  248. // the module is installed.
  249. if (module_exists('entity')) {
  250. return entity_access('view', $entity_type, $entity);
  251. }
  252. return TRUE;
  253. }
  254. /**
  255. * The contact form page.
  256. */
  257. function email_mail_page($object_type, $object_id, $field_name) {
  258. if (!is_numeric($object_id)) {
  259. return MENU_NOT_FOUND;
  260. }
  261. // Verify this is an email field.
  262. $field_info = field_info_field($field_name);
  263. if (!isset($field_info) || $field_info['type'] != 'email') {
  264. return MENU_NOT_FOUND;
  265. }
  266. // Check that the entity exists.
  267. $objects = entity_load($object_type, array($object_id));
  268. if (!isset($objects[$object_id])) {
  269. return MENU_NOT_FOUND;
  270. }
  271. $object = $objects[$object_id];
  272. // Check that the entity has the email field.
  273. if (!isset($object->$field_name)) {
  274. return MENU_NOT_FOUND;
  275. }
  276. // Check if the current user has access to the entity and to the field.
  277. if (!email_mail_page_access($object_type, $object, $field_info)) {
  278. return MENU_ACCESS_DENIED;
  279. }
  280. //use the first email address as receiver
  281. $field = array_pop($object->$field_name);
  282. foreach ($field as $delta => $item) {
  283. if (!empty($item['email'])) {
  284. $email = $item['email'];
  285. break;
  286. }
  287. }
  288. //verify that the email address is not empty
  289. if (empty($email)) {
  290. return MENU_NOT_FOUND;
  291. }
  292. $entity_info = entity_extract_ids($object_type, $object);
  293. $settings = field_info_instance($object_type, $field_name, $entity_info[2]);
  294. $found = FALSE;
  295. foreach ($settings['display'] as $display_name => $display_data) {
  296. if (isset($display_data['type']) && ($display_data['type'] === 'email_contact')) {
  297. $found = TRUE;
  298. break;
  299. }
  300. }
  301. if (!$found) {
  302. return MENU_NOT_FOUND;
  303. }
  304. if (!flood_is_allowed('email', variable_get('email_hourly_threshold', 3))) {
  305. return t("You cannot send more than %number messages per hour. Please try again later.", array('%number' => variable_get('email_hourly_threshold', 3)));
  306. }
  307. return drupal_get_form('email_mail_page_form', $object_type, $object_id, $field_name, $email);
  308. }
  309. /**
  310. * Contact form
  311. */
  312. function email_mail_page_form($form, $form_state, $object_type, $object_id, $field_name, $email) {
  313. global $user;
  314. $form['object_id'] = array(
  315. '#type' => 'value',
  316. '#value' => $object_id,
  317. );
  318. $form['object_type'] = array(
  319. '#type' => 'value',
  320. '#value' => $object_type,
  321. );
  322. $form['field_name'] = array(
  323. '#type' => 'value',
  324. '#value' => $field_name,
  325. );
  326. $form['email'] = array(
  327. '#type' => 'value',
  328. '#value' => $email,
  329. );
  330. $form['name'] = array(
  331. '#type' => 'textfield',
  332. '#title' => t('Your name'),
  333. '#maxlength' => 255,
  334. '#default_value' => $user->uid ? $user->name : '',
  335. '#required' => TRUE,
  336. );
  337. $form['mail'] = array(
  338. '#type' => 'textfield',
  339. '#title' => t('Your e-mail address'),
  340. '#maxlength' => 255,
  341. '#default_value' => $user->uid ? $user->mail : '',
  342. '#required' => TRUE,
  343. );
  344. $form['subject'] = array(
  345. '#type' => 'textfield',
  346. '#title' => t('Subject'),
  347. '#maxlength' => 255,
  348. '#required' => TRUE,
  349. );
  350. $form['message'] = array(
  351. '#type' => 'textarea',
  352. '#title' => t('Message'),
  353. '#required' => TRUE,
  354. );
  355. $form['submit'] = array(
  356. '#type' => 'submit',
  357. '#value' => t('Send e-mail'),
  358. '#validate' => array('email_mail_page_form_validate'),
  359. '#submit' => array('email_mail_page_form_submit'),
  360. );
  361. return $form;
  362. }
  363. /**
  364. * Validate the site-wide contact page form submission.
  365. */
  366. function email_mail_page_form_validate($form, &$form_state) {
  367. if (!valid_email_address($form_state['values']['mail'])) {
  368. form_set_error('mail', t('You must enter a valid e-mail address.'));
  369. }
  370. if (preg_match("/\r|\n/", $form_state['values']['subject'])) {
  371. form_set_error('subject', t('The subject cannot contain linebreaks.'));
  372. watchdog('mail', 'Email injection exploit attempted in email form subject: ' . check_plain($form_state['values']['subject']), WATCHDOG_NOTICE);
  373. }
  374. }
  375. /**
  376. * Process the site-wide contact page form submission.
  377. */
  378. function email_mail_page_form_submit($form, &$form_state) {
  379. $object_type = $form_state['values']['object_type'];
  380. $object_id = $form_state['values']['object_id'];
  381. $field_name = $form_state['values']['field_name'];
  382. $email = $form_state['values']['email'];
  383. // Load entity
  384. $objects = entity_load($object_type, array($object_id));
  385. $object = $objects[$object_id];
  386. $object_info = entity_get_info($object_type);
  387. // E-mail address of the sender: as the form field is a text field,
  388. // all instances of \r and \n have been automatically stripped from it.
  389. $from = $form_state['values']['mail'];
  390. $params['object'] = $object;
  391. $params['subject'] = $form_state['values']['subject'];
  392. $params['name'] = $form_state['values']['name'];
  393. $params['message'] = $form_state['values']['message'];
  394. $path = "";
  395. if (isset($object_info['path callback']) && function_exists($object_info['path callback'])) {
  396. $path = $object_info['path callback']($object);
  397. }
  398. $params['url'] = url($path, array('absolute' => TRUE));
  399. // Send the e-mail to the recipients:
  400. drupal_mail('email', 'contact', $email, language_default(), $params, $from);
  401. // Log the operation:
  402. flood_register_event('email');
  403. watchdog('mail', '%name-from sent an e-mail at %form.', array('%name-from' => $form_state['values']['name'], '%form' => url($_GET['q'], array('absolute' => TRUE))));
  404. drupal_set_message(t('Your message has been sent.'));
  405. $form_state['redirect'] = $path;
  406. }
  407. /**
  408. * Implements hook_mail().
  409. */
  410. function email_mail($key, &$message, $params) {
  411. $language = $message['language'];
  412. switch ($key) {
  413. case 'contact':
  414. // Compose the body:
  415. $message['body'][] = t('@name sent a message using the contact form at @url.', array('@name' => $params['name'], '@url' => $params['url']), array('langcode' => $language->language));
  416. $message['body'][] = $params['message'];
  417. $message['subject'] = "";
  418. // Include the title of the entity, if one exists
  419. $object = $params['object'];
  420. if (isset($object->title) && !empty($object->title)) {
  421. $message['subject'] = "[" . check_plain(preg_replace("/\r|\n/", '', $object->title)) . "]";
  422. }
  423. $message['subject'] .= " " . check_plain($params['subject']);
  424. break;
  425. }
  426. }
  427. //TODO Token support
  428. /**
  429. * Implements hook_token_list().
  430. *
  431. function email_token_list($type = 'all') {
  432. if ($type == 'field' || $type == 'all') {
  433. $tokens['email']['raw'] = t('Raw email address');
  434. $tokens['email']['formatted'] = t('Formatted email address');
  435. return $tokens;
  436. }
  437. }
  438. /**
  439. * Implements hook token_values().
  440. *
  441. function email_token_values($type, $object = NULL, $options = array()) {
  442. if ($type == 'field') {
  443. $item = $object[0];
  444. $tokens['raw'] = $item['email'];
  445. $tokens['formatted'] = $item['view'];
  446. return $tokens;
  447. }
  448. }
  449. */
  450. /**
  451. * Settings for contact form
  452. */
  453. function email_admin_settings() {
  454. $form['email_hourly_threshold'] = array('#type' => 'select',
  455. '#title' => t('Hourly threshold for a CCK Email contact form'),
  456. '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50)),
  457. '#default_value' => variable_get('email_hourly_threshold', 3),
  458. '#description' => t('The maximum number of contact form submissions a user can perform per hour.'),
  459. );
  460. return system_settings_form($form);
  461. }
  462. /**
  463. * Implements hook_content_migrate_instance_alter().
  464. *
  465. * D6-D7 upgrade
  466. * fixes new formatter names
  467. */
  468. function email_content_migrate_instance_alter(&$instance_value, &$field_value) {
  469. if ($field_value['module'] == 'email') {
  470. foreach ($instance_value['display'] as $context => $settings) {
  471. if (in_array($instance_value['display'][$context]['type'], array('default', 'plain', 'contact', 'spamspan'))) {
  472. $instance_value['display'][$context]['type'] = 'email_' . $instance_value['display'][$context]['type'];
  473. }
  474. }
  475. }
  476. }