date_all_day.module 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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. * @param array $field, the field definition for this date field.
  87. * @param string $which, which value to return, 'date1' or 'date2' .
  88. * @param object $date1, a date/time object for the 'start' date.
  89. * @param object $date2, a date/time object for the 'end' date.
  90. * @param string $format
  91. * @param object $entity, the node this date comes from (may be incomplete, always contains nid).
  92. * @param object $view, the view this node comes from, if applicable.
  93. * @return formatted date.
  94. */
  95. function theme_date_all_day($vars) {
  96. $field = $vars['field'];
  97. $instance = $vars['instance'];
  98. $which = $vars['which'];
  99. $date1 = $vars['date1'];
  100. $date2 = $vars['date2'];
  101. $format = $vars['format'];
  102. $entity = $vars['entity'];
  103. $view = !empty($vars['view']) ? $vars['view'] : NULL;
  104. if (empty($date1) || !is_object($date1) || $format == 'format_interval') {
  105. return;
  106. }
  107. if (empty($date2)) {
  108. $date2 = $date1;
  109. }
  110. $suffix = '';
  111. if (!date_has_time($field['settings']['granularity'])) {
  112. $format = date_limit_format($format, array('year', 'month', 'day'));
  113. }
  114. else {
  115. $format_granularity = date_format_order($format);
  116. $format_has_time = FALSE;
  117. if (in_array('hour', $format_granularity)) {
  118. $format_has_time = TRUE;
  119. }
  120. $all_day = date_all_day_field($field, $instance, $date1, $date2);
  121. if ($all_day && $format_has_time) {
  122. $format = date_limit_format($format, array('year', 'month', 'day'));
  123. $suffix = ' ' . theme('date_all_day_label');
  124. }
  125. }
  126. return trim(date_format_date($$which, 'custom', $format) . $suffix);
  127. }
  128. /**
  129. * Theme the way an 'all day' label will look.
  130. */
  131. function theme_date_all_day_label() {
  132. return '(' . t('All day', array(), array('context' => 'datetime')) .')';
  133. }
  134. /**
  135. * Determine if a Start/End date combination qualify as 'All day'.
  136. *
  137. * @param array $field, the field definition for this date field.
  138. * @param object $date1, a date/time object for the 'Start' date.
  139. * @param object $date2, a date/time object for the 'End' date.
  140. * @return TRUE or FALSE.
  141. */
  142. function date_all_day_field($field, $instance, $date1, $date2 = NULL) {
  143. if (empty($date1) || !is_object($date1)) {
  144. return FALSE;
  145. }
  146. elseif (!date_has_time($field['settings']['granularity'])) {
  147. return TRUE;
  148. }
  149. if (empty($date2)) {
  150. $date2 = $date1;
  151. }
  152. $granularity = date_granularity_precision($field['settings']['granularity']);
  153. $increment = isset($instance['widget']['settings']['increment']) ? $instance['widget']['settings']['increment'] : 1;
  154. return date_is_all_day(date_format($date1, DATE_FORMAT_DATETIME), date_format($date2, DATE_FORMAT_DATETIME), $granularity, $increment);
  155. }
  156. /**
  157. * Implements hook_date_combo_process_alter().
  158. *
  159. * This hook lets us make changes to the date_combo element.
  160. */
  161. function date_all_day_date_combo_process_alter(&$element, &$form_state, $context) {
  162. $field = $context['field'];
  163. $instance = $context['instance'];
  164. // Add the all_day checkbox to the combo element.
  165. if (!empty($instance['widget']['settings']['display_all_day'])) {
  166. $parents = $element['#parents'];
  167. $first_parent = array_shift($parents);
  168. $all_day_id = $first_parent . '[' . implode('][', $parents) . '][all_day]';;
  169. foreach (array('value', 'value2') as $key) {
  170. if (array_key_exists($key, $element)) {
  171. $element[$key]['#date_all_day_id'] = $all_day_id;
  172. }
  173. }
  174. $from = $element['#default_value']['value'];
  175. $to = !empty($element['#default_value']['value2']) ? $element['#default_value']['value2'] : $element['#default_value']['value'];
  176. $date_is_all_day = date_is_all_day($from, $to);
  177. $all_day = !empty($form_state['values']['all_day']) || $date_is_all_day;
  178. $element['all_day'] = array(
  179. '#title' => t('All Day'),
  180. '#type' => 'checkbox',
  181. '#default_value' => $all_day,
  182. '#weight' => -21,
  183. '#prefix' => '<div class="date-float">',
  184. '#suffix' => '</div>',
  185. );
  186. }
  187. // Set all_day to 0 for all other date fields.
  188. else {
  189. $form['all_day']['#type'] = 'hidden';
  190. $form['all_day']['#value'] = 0;
  191. }
  192. }
  193. /**
  194. * Implements hook_date_text_process_alter().
  195. *
  196. * This hook lets us make changes to the date_select widget.
  197. */
  198. function date_all_day_date_text_process_alter(&$element, &$form_state, $context) {
  199. $all_day_id = !empty($element['#date_all_day_id']) ? $element['#date_all_day_id'] : '';
  200. if ($all_day_id != '') {
  201. // All Day handling on text dates works only if the user leaves the time out of the input value.
  202. // There is no element to hide or show.
  203. }
  204. }
  205. /**
  206. * Implements hook_date_select_process_alter().
  207. *
  208. * This hook lets us make changes to the date_select widget.
  209. */
  210. function date_all_day_date_select_process_alter(&$element, &$form_state, $context) {
  211. // Hide or show this element in reaction to the all_day status for this element.
  212. $all_day_id = !empty($element['#date_all_day_id']) ? $element['#date_all_day_id'] : '';
  213. if ($all_day_id != '') {
  214. foreach(array('hour', 'minute', 'second', 'ampm') as $field) {
  215. if (array_key_exists($field, $element)) {
  216. $element[$field]['#states'] = array(
  217. 'visible' => array(
  218. 'input[name="' . $all_day_id . '"]' => array('checked' => FALSE),
  219. ));
  220. }
  221. }
  222. }
  223. }
  224. /**
  225. * Implements hook_date_popup_process_alter().
  226. *
  227. * This hook lets us make changes to the date_popup element.
  228. */
  229. function date_all_day_date_popup_process_alter(&$element, &$form_state, $context) {
  230. // Hide or show this element in reaction to the all_day status for this element.
  231. $all_day_id = !empty($element['#date_all_day_id']) ? $element['#date_all_day_id'] : '';
  232. if ($all_day_id != '' && array_key_exists('time', $element)) {
  233. $element['time']['#states'] = array(
  234. 'visible' => array(
  235. 'input[name="' . $all_day_id . '"]' => array('checked' => FALSE),
  236. ));
  237. }
  238. }
  239. /**
  240. * Implements hook_date_select_pre_validate_alter().
  241. *
  242. * This hook lets us alter the element or the form_state before the rest
  243. * of the date_select validation gets fired.
  244. */
  245. function date_all_day_date_text_pre_validate_alter(&$element, &$form_state, &$input) {
  246. // Let Date module massage the format for all day values so they will pass validation.
  247. // The All day flag, if used, actually exists on the parent element.
  248. date_all_day_value($element, $form_state);
  249. }
  250. /**
  251. * Implements hook_date_select_pre_validate_alter().
  252. *
  253. * This hook lets us alter the element or the form_state before the rest
  254. * of the date_select validation gets fired.
  255. */
  256. function date_all_day_date_select_pre_validate_alter(&$element, &$form_state, &$input) {
  257. // Let Date module massage the format for all day values so they will pass validation.
  258. // The All day flag, if used, actually exists on the parent element.
  259. date_all_day_value($element, $form_state);
  260. }
  261. /**
  262. * Implements hook_date_select_pre_validate_alter().
  263. *
  264. * This hook lets us alter the element or the form_state before the rest
  265. * of the date_popup validation gets fired.
  266. */
  267. function date_all_day_date_popup_pre_validate_alter(&$element, &$form_state, &$input) {
  268. // Let Date module massage the format for all day values so they will pass validation.
  269. // The All day flag, if used, actually exists on the parent element.
  270. date_all_day_value($element, $form_state);
  271. }
  272. /**
  273. * A helper function to check if the all day flag is set on the parent of an
  274. * element, and adjust the date_format accordingly so the missing time will
  275. * not cause validation errors.
  276. */
  277. function date_all_day_value(&$element, $form_state) {
  278. if (!empty($element['#date_all_day_id'])) {
  279. $parents = $element['#parents'];
  280. array_pop($parents);
  281. $parent_element = drupal_array_get_nested_value($form_state['values'], $parents);
  282. if (!empty($parent_element) && !empty($parent_element['all_day'])) {
  283. $element['#date_format'] = date_part_format('date', $element['#date_format']);
  284. return $parent_element['all_day'];
  285. }
  286. }
  287. return FALSE;
  288. }
  289. /**
  290. * Implements hook_date_combo_pre_validate_alter().
  291. *
  292. * This hook lets us alter the element or the form_state before the rest
  293. * of the date_combo validation gets fired.
  294. */
  295. function date_all_day_date_combo_pre_validate_alter(&$element, &$form_state, $context) {
  296. if (!empty($context['item']['all_day'])) {
  297. $field = $context['field'];
  298. // If we have an all day flag on this date and the time is empty,
  299. // change the format to match the input value so we don't get validation errors.
  300. $element['#date_is_all_day'] = TRUE;
  301. $element['value']['#date_format'] = date_part_format('date', $element['value']['#date_format']);
  302. if (!empty($field['settings']['todate'])) {
  303. $element['value2']['#date_format'] = date_part_format('date', $element['value2']['#date_format']);
  304. }
  305. }
  306. }
  307. /**
  308. * Implements hook_date_combo_validate_date_start_alter().
  309. *
  310. * This hook lets us alter the local date objects created by the date_combo validation
  311. * before they are converted back to the database timezone and stored.
  312. */
  313. function date_all_day_date_combo_validate_date_start_alter(&$date, &$form_state, $context) {
  314. // If this is an 'All day' value, set the time to midnight.
  315. if (!empty($context['element']['#date_is_all_day'])) {
  316. $date->setTime(0, 0, 0);
  317. }
  318. }
  319. /**
  320. * Implements hook_date_combo_validate_date_end_alter().
  321. *
  322. * This hook lets us alter the local date objects created by the date_combo validation
  323. * before they are converted back to the database timezone and stored.
  324. */
  325. function date_all_day_date_combo_validate_date_end_alter(&$date, &$form_state, $context) {
  326. // If this is an 'All day' value, set the time to midnight.
  327. if (!empty($context['element']['#date_is_all_day'])) {
  328. $date->setTime(0, 0, 0);
  329. }
  330. }
  331. /**
  332. * Implements hook_field_widget_info_alter().
  333. *
  334. * This Field API hook lets us add a new setting to the widgets.
  335. */
  336. function date_all_day_field_widget_info_alter(&$info) {
  337. // Add a setting to a widget type.
  338. $info['date_select']['settings'] += array(
  339. 'display_all_day' => 0,
  340. );
  341. $info['date_text']['settings'] += array(
  342. 'display_all_day' => 0,
  343. );
  344. if (module_exists('date_popup')) {
  345. $info['date_popup']['settings'] += array(
  346. 'display_all_day' => 0,
  347. );
  348. }
  349. }
  350. /**
  351. * Implements hook_date_field_widget_settings_form_alter().
  352. *
  353. * This hook lets us alter the field settings form by adding a place
  354. * to set the value added above.
  355. */
  356. function date_all_day_date_field_widget_settings_form_alter(&$form, $context) {
  357. $field = $context['field'];
  358. $instance = $context['instance'];
  359. $settings = $instance['widget']['settings'];
  360. $form['display_all_day'] = array(
  361. '#type' => 'checkbox',
  362. '#title' => t('Display all day checkbox'),
  363. '#default_value' => $settings['display_all_day'],
  364. '#description' => t("Determines whether to display the 'All Day' checkbox to the user."),
  365. '#weight' => 8,
  366. '#fieldset' => 'date_format',
  367. );
  368. // Hide the option to use the all day checkbox for dates with no time.
  369. if (!date_has_time($field['settings']['granularity'])) {
  370. $form['display_all_day']['#type'] = 'hidden';
  371. $form['display_all_day']['#value'] = 0;
  372. }
  373. }