email.module 15 KB

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