date_all_day.module 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. <?php
  2. /**
  3. * @file
  4. * Adds All Day functionality to the Date field.
  5. *
  6. * This module implements a number of hooks in the Date field and
  7. * Date api element processing and to add an All Day checkbox to
  8. * date widgets, and also adds an All Day theme and
  9. * All Day information to the formatting.
  10. *
  11. * Keep in mind that the process hooks are fired from the top down,
  12. * first date_combo, then the date_select or date_popup.
  13. *
  14. * Validation fires from the bottom up, first date_select and
  15. * date_popup, then date_combo.
  16. */
  17. /**
  18. * Implements hook_theme().
  19. */
  20. function date_all_day_theme() {
  21. $themes = array(
  22. 'date_all_day' => array(
  23. 'variables' => array(
  24. 'field' => NULL,
  25. 'instance' => NULL,
  26. 'which' => NULL,
  27. 'date1' => NULL,
  28. 'date2' => NULL,
  29. 'format' => NULL,
  30. 'entity_type' => NULL,
  31. 'entity' => NULL,
  32. 'view' => NULL,
  33. ),
  34. ),
  35. 'date_all_day_label' => array(
  36. 'variables' => array(),
  37. ),
  38. );
  39. return $themes;
  40. }
  41. /**
  42. * Implements hook_date_formatter_dates_alter().
  43. *
  44. * This allows us to alter the $dates array created
  45. * by date_formatter_process.
  46. */
  47. function date_all_day_date_formatter_dates_alter(&$dates, $context) {
  48. $field = $context['field'];
  49. $instance = $context['instance'];
  50. $format = $context['format'];
  51. $entity_type = $context['entity_type'];
  52. $entity = $context['entity'];
  53. $date1 = $dates['value']['local']['object'];
  54. $date2 = $dates['value2']['local']['object'];
  55. $is_all_day = date_all_day_field($field, $instance, $date1, $date2);
  56. $all_day1 = '';
  57. $all_day2 = '';
  58. if ($format != 'format_interval' && $is_all_day) {
  59. $all_day1 = theme('date_all_day', array(
  60. 'field' => $field,
  61. 'instance' => $instance,
  62. 'which' => 'date1',
  63. 'date1' => $date1,
  64. 'date2' => $date2,
  65. 'format' => $format,
  66. 'entity_type' => $entity_type,
  67. 'entity' => $entity));
  68. $all_day2 = theme('date_all_day', array(
  69. 'field' => $field,
  70. 'instance' => $instance,
  71. 'which' => 'date2',
  72. 'date1' => $date1,
  73. 'date2' => $date2,
  74. 'format' => $format,
  75. 'entity_type' => $entity_type,
  76. 'entity' => $entity));
  77. $dates['value']['formatted_time'] = theme('date_all_day_label');
  78. $dates['value2']['formatted_time'] = theme('date_all_day_label');
  79. $dates['value']['formatted'] = $all_day1;
  80. $dates['value2']['formatted'] = $all_day2;
  81. }
  82. }
  83. /**
  84. * Adjust start/end date format to account for 'all day' .
  85. *
  86. * @params array $field
  87. * The field definition for this date field.
  88. *
  89. * @params string $which
  90. * Which value to return, 'date1' or 'date2'.
  91. *
  92. * @params object $date1
  93. * A date/time object for the 'start' date.
  94. *
  95. * @params object $date2
  96. * A date/time object for the 'end' date.
  97. *
  98. * @params string $format
  99. * A date/time format
  100. *
  101. * @params object $entity
  102. * The node this date comes from (may be incomplete, always contains nid).
  103. *
  104. * @params object $view
  105. * The view this node comes from, if applicable.
  106. *
  107. * @return string
  108. * Formatted date.
  109. */
  110. function theme_date_all_day($vars) {
  111. $field = $vars['field'];
  112. $instance = $vars['instance'];
  113. $which = $vars['which'];
  114. $date1 = $vars['date1'];
  115. $date2 = $vars['date2'];
  116. $format = $vars['format'];
  117. $entity = $vars['entity'];
  118. $view = !empty($vars['view']) ? $vars['view'] : NULL;
  119. if (empty($date1) || !is_object($date1) || $format == 'format_interval') {
  120. return;
  121. }
  122. if (empty($date2)) {
  123. $date2 = $date1;
  124. }
  125. $suffix = '';
  126. if (!date_has_time($field['settings']['granularity'])) {
  127. $format = date_limit_format($format, array('year', 'month', 'day'));
  128. }
  129. else {
  130. $format_granularity = date_format_order($format);
  131. $format_has_time = FALSE;
  132. if (in_array('hour', $format_granularity)) {
  133. $format_has_time = TRUE;
  134. }
  135. $all_day = date_all_day_field($field, $instance, $date1, $date2);
  136. if ($all_day && $format_has_time) {
  137. $format = date_limit_format($format, array('year', 'month', 'day'));
  138. $suffix = ' ' . theme('date_all_day_label');
  139. }
  140. }
  141. return trim(date_format_date($$which, 'custom', $format) . $suffix);
  142. }
  143. /**
  144. * Theme the way an 'all day' label will look.
  145. */
  146. function theme_date_all_day_label() {
  147. return '(' . t('All day', array(), array('context' => 'datetime')) . ')';
  148. }
  149. /**
  150. * Determine if a Start/End date combination qualify as 'All day'.
  151. *
  152. * @param array $field
  153. * The field definition for this date field.
  154. *
  155. * @param array $instance
  156. * The field instance for this date field.
  157. *
  158. * @param object $date1
  159. * A date/time object for the 'Start' date.
  160. *
  161. * @param object $date2
  162. * A date/time object for the 'End' date.
  163. *
  164. * @return bool
  165. * TRUE or FALSE.
  166. */
  167. function date_all_day_field($field, $instance, $date1, $date2 = NULL) {
  168. if (empty($date1) || !is_object($date1)) {
  169. return FALSE;
  170. }
  171. elseif (!date_has_time($field['settings']['granularity'])) {
  172. return TRUE;
  173. }
  174. if (empty($date2)) {
  175. $date2 = $date1;
  176. }
  177. $granularity = date_granularity_precision($field['settings']['granularity']);
  178. $increment = isset($instance['widget']['settings']['increment']) ? $instance['widget']['settings']['increment'] : 1;
  179. return date_is_all_day(date_format($date1, DATE_FORMAT_DATETIME), date_format($date2, DATE_FORMAT_DATETIME), $granularity, $increment);
  180. }
  181. /**
  182. * Implements hook_date_combo_process_alter().
  183. *
  184. * This hook lets us make changes to the date_combo element.
  185. */
  186. function date_all_day_date_combo_process_alter(&$element, &$form_state, $context) {
  187. $field = $context['field'];
  188. $instance = $context['instance'];
  189. // Add the all_day checkbox to the combo element.
  190. if (!empty($instance['widget']['settings']['display_all_day'])) {
  191. $parents = $element['#parents'];
  192. $first_parent = array_shift($parents);
  193. $all_day_id = $first_parent . '[' . implode('][', $parents) . '][all_day]';;
  194. foreach (array('value', 'value2') as $key) {
  195. if (array_key_exists($key, $element)) {
  196. $element[$key]['#date_all_day_id'] = $all_day_id;
  197. }
  198. }
  199. $from = $element['#default_value']['value'];
  200. $to = !empty($element['#default_value']['value2']) ? $element['#default_value']['value2'] : $element['#default_value']['value'];
  201. $date_is_all_day = date_is_all_day($from, $to);
  202. $all_day = !empty($form_state['values']['all_day']) || $date_is_all_day;
  203. $element['all_day'] = array(
  204. '#title' => t('All Day'),
  205. '#type' => 'checkbox',
  206. '#default_value' => $all_day,
  207. '#weight' => -21,
  208. '#prefix' => '<div class="date-float">',
  209. '#suffix' => '</div>',
  210. );
  211. }
  212. // Set all_day to 0 for all other date fields.
  213. else {
  214. $form['all_day']['#type'] = 'hidden';
  215. $form['all_day']['#value'] = 0;
  216. }
  217. }
  218. /**
  219. * Implements hook_date_text_process_alter().
  220. *
  221. * This hook lets us make changes to the date_select widget.
  222. */
  223. function date_all_day_date_text_process_alter(&$element, &$form_state, $context) {
  224. $all_day_id = !empty($element['#date_all_day_id']) ? $element['#date_all_day_id'] : '';
  225. if ($all_day_id != '') {
  226. // All Day handling on text dates works only
  227. // if the user leaves the time out of the input value.
  228. // There is no element to hide or show.
  229. }
  230. }
  231. /**
  232. * Implements hook_date_select_process_alter().
  233. *
  234. * This hook lets us make changes to the date_select widget.
  235. */
  236. function date_all_day_date_select_process_alter(&$element, &$form_state, $context) {
  237. // Hide or show this element in reaction
  238. // to the all_day status for this element.
  239. $all_day_id = !empty($element['#date_all_day_id']) ? $element['#date_all_day_id'] : '';
  240. if ($all_day_id != '') {
  241. foreach (array('hour', 'minute', 'second', 'ampm') as $field) {
  242. if (array_key_exists($field, $element)) {
  243. $element[$field]['#states'] = array(
  244. 'visible' => array(
  245. 'input[name="' . $all_day_id . '"]' => array('checked' => FALSE),
  246. ));
  247. }
  248. }
  249. }
  250. }
  251. /**
  252. * Implements hook_date_popup_process_alter().
  253. *
  254. * This hook lets us make changes to the date_popup element.
  255. */
  256. function date_all_day_date_popup_process_alter(&$element, &$form_state, $context) {
  257. // Hide or show this element in reaction to
  258. // the all_day status for this element.
  259. $all_day_id = !empty($element['#date_all_day_id']) ? $element['#date_all_day_id'] : '';
  260. if ($all_day_id != '' && array_key_exists('time', $element)) {
  261. $element['time']['#states'] = array(
  262. 'visible' => array(
  263. 'input[name="' . $all_day_id . '"]' => array('checked' => FALSE),
  264. ));
  265. }
  266. }
  267. /**
  268. * Implements hook_date_select_pre_validate_alter().
  269. *
  270. * This hook lets us alter the element or the form_state before the rest
  271. * of the date_select validation gets fired.
  272. */
  273. function date_all_day_date_text_pre_validate_alter(&$element, &$form_state, &$input) {
  274. // Let Date module massage the format for all day
  275. // values so they will pass validation.
  276. // The All day flag, if used, actually exists on the parent element.
  277. date_all_day_value($element, $form_state);
  278. }
  279. /**
  280. * Implements hook_date_select_pre_validate_alter().
  281. *
  282. * This hook lets us alter the element or the form_state before the rest
  283. * of the date_select validation gets fired.
  284. */
  285. function date_all_day_date_select_pre_validate_alter(&$element, &$form_state, &$input) {
  286. // Let Date module massage the format for all
  287. // day values so they will pass validation.
  288. // The All day flag, if used, actually exists on the parent element.
  289. date_all_day_value($element, $form_state);
  290. }
  291. /**
  292. * Implements hook_date_select_pre_validate_alter().
  293. *
  294. * This hook lets us alter the element or the form_state before the rest
  295. * of the date_popup validation gets fired.
  296. */
  297. function date_all_day_date_popup_pre_validate_alter(&$element, &$form_state, &$input) {
  298. // Let Date module massage the format for all
  299. // day values so they will pass validation.
  300. // The All day flag, if used, actually exists on the parent element.
  301. date_all_day_value($element, $form_state);
  302. }
  303. /**
  304. * A helper function date_all_day_value().
  305. *
  306. * To check if the all day flag is set on the parent of an
  307. * element, and adjust the date_format accordingly so the missing time will
  308. * not cause validation errors.
  309. */
  310. function date_all_day_value(&$element, $form_state) {
  311. if (!empty($element['#date_all_day_id'])) {
  312. $parents = $element['#parents'];
  313. array_pop($parents);
  314. $parent_element = drupal_array_get_nested_value($form_state['values'], $parents);
  315. if (!empty($parent_element) && !empty($parent_element['all_day'])) {
  316. $element['#date_format'] = date_part_format('date', $element['#date_format']);
  317. return $parent_element['all_day'];
  318. }
  319. }
  320. return FALSE;
  321. }
  322. /**
  323. * Implements hook_date_combo_pre_validate_alter().
  324. *
  325. * This hook lets us alter the element or the form_state before the rest
  326. * of the date_combo validation gets fired.
  327. */
  328. function date_all_day_date_combo_pre_validate_alter(&$element, &$form_state, $context) {
  329. if (!empty($context['item']['all_day'])) {
  330. $field = $context['field'];
  331. // If we have an all day flag on this date and the time is empty,
  332. // change the format to match the input value
  333. // so we don't get validation errors.
  334. $element['#date_is_all_day'] = TRUE;
  335. $element['value']['#date_format'] = date_part_format('date', $element['value']['#date_format']);
  336. if (!empty($field['settings']['todate'])) {
  337. $element['value2']['#date_format'] = date_part_format('date', $element['value2']['#date_format']);
  338. }
  339. }
  340. }
  341. /**
  342. * Implements hook_date_combo_validate_date_start_alter().
  343. *
  344. * This hook lets us alter the local date objects
  345. * created by the date_combo validation before they are
  346. * converted back to the database timezone and stored.
  347. */
  348. function date_all_day_date_combo_validate_date_start_alter(&$date, &$form_state, $context) {
  349. // If this is an 'All day' value, set the time to midnight.
  350. if (!empty($context['element']['#date_is_all_day'])) {
  351. $date->setTime(0, 0, 0);
  352. }
  353. }
  354. /**
  355. * Implements hook_date_combo_validate_date_end_alter().
  356. *
  357. * This hook lets us alter the local date objects
  358. * created by the date_combo validation before
  359. * they are converted back to the database timezone and stored.
  360. */
  361. function date_all_day_date_combo_validate_date_end_alter(&$date, &$form_state, $context) {
  362. // If this is an 'All day' value, set the time to midnight.
  363. if (!empty($context['element']['#date_is_all_day'])) {
  364. $date->setTime(0, 0, 0);
  365. }
  366. }
  367. /**
  368. * Implements hook_field_widget_info_alter().
  369. *
  370. * This Field API hook lets us add a new setting to the widgets.
  371. */
  372. function date_all_day_field_widget_info_alter(&$info) {
  373. // Add a setting to a widget type.
  374. $info['date_select']['settings'] += array(
  375. 'display_all_day' => 0,
  376. );
  377. $info['date_text']['settings'] += array(
  378. 'display_all_day' => 0,
  379. );
  380. if (module_exists('date_popup')) {
  381. $info['date_popup']['settings'] += array(
  382. 'display_all_day' => 0,
  383. );
  384. }
  385. }
  386. /**
  387. * Implements hook_date_field_widget_settings_form_alter().
  388. *
  389. * This hook lets us alter the field settings form by adding a place
  390. * to set the value added above.
  391. */
  392. function date_all_day_date_field_widget_settings_form_alter(&$form, $context) {
  393. $field = $context['field'];
  394. $instance = $context['instance'];
  395. $settings = $instance['widget']['settings'];
  396. $form['display_all_day'] = array(
  397. '#type' => 'checkbox',
  398. '#title' => t('Display all day checkbox'),
  399. '#default_value' => $settings['display_all_day'],
  400. '#description' => t("Determines whether to display the 'All Day' checkbox to the user."),
  401. '#weight' => 8,
  402. '#fieldset' => 'date_format',
  403. );
  404. // Hide the option to use the all day checkbox for dates with no time.
  405. if (!date_has_time($field['settings']['granularity'])) {
  406. $form['display_all_day']['#type'] = 'hidden';
  407. $form['display_all_day']['#value'] = 0;
  408. }
  409. }