date.theme 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. <?php
  2. /**
  3. * @file
  4. * Theme functions.
  5. */
  6. /**
  7. * @addtogroup themeable
  8. * @{
  9. *
  10. * Formatter themes
  11. */
  12. /**
  13. * Returns HTML for a date element formatted as a Start/End combination.
  14. *
  15. * $entity->date_id
  16. * If set, this will show only an individual date on a field with
  17. * multiple dates. The value should be a string that contains
  18. * the following values, separated with periods:
  19. * - module name of the module adding the item
  20. * - node nid
  21. * - field name
  22. * - delta value of the field to be displayed
  23. * - other information the module's custom theme might need.
  24. *
  25. * Used by the calendar module and available for other uses.
  26. * example: 'date.217.field_date.3.test'
  27. *
  28. * $entity->date_repeat_show
  29. * If TRUE, tells the theme to show all the computed values of a repeating
  30. * date. If not TRUE or not set, only the start date and the repeat rule
  31. * will be displayed.
  32. *
  33. * $dates['format']
  34. * The format string used on these dates
  35. * $dates['value']['local']['object']
  36. * The local date object for the Start date
  37. * $dates['value2']['local']['object']
  38. * The local date object for the End date
  39. * $dates['value']['local']['datetime']
  40. * The datetime value of the Start date database (GMT) value
  41. * $dates['value2']['local']['datetime']
  42. * The datetime value of the End date database (GMT) value
  43. * $dates['value']['formatted']
  44. * Formatted Start date, i.e. 'February 15, 2007 2:00 pm';
  45. * $dates['value']['formatted_date']
  46. * Only the date part of the formatted Start date
  47. * $dates['value']['formatted_time']
  48. * Only the time part of the formatted Start date
  49. * $dates['value2']['formatted']
  50. * Formatted End date, i.e. 'February 15, 2007 6:00 pm';
  51. * $dates['value2']['formatted_date']
  52. * Only the date part of the formatted End date
  53. * $dates['value2']['formatted_time']
  54. * Only the time part of the formatted End date
  55. */
  56. function theme_date_display_combination($variables) {
  57. static $repeating_ids = array();
  58. $entity_type = $variables['entity_type'];
  59. $entity = $variables['entity'];
  60. $field = $variables['field'];
  61. $instance = $variables['instance'];
  62. $langcode = $variables['langcode'];
  63. $item = $variables['item'];
  64. $delta = $variables['delta'];
  65. $display = $variables['display'];
  66. $field_name = $field['field_name'];
  67. $formatter = $display['type'];
  68. $options = $display['settings'];
  69. $dates = $variables['dates'];
  70. $attributes = $variables['attributes'];
  71. $rdf_mapping = $variables['rdf_mapping'];
  72. $add_rdf = $variables['add_rdf'];
  73. $microdata = $variables['microdata'];
  74. $add_microdata = $variables['add_microdata'];
  75. $precision = date_granularity_precision($field['settings']['granularity']);
  76. $output = '';
  77. // If date_id is set for this field and delta doesn't match, don't display it.
  78. if (!empty($entity->date_id)) {
  79. foreach ((array) $entity->date_id as $key => $id) {
  80. list($module, $nid, $field_name, $item_delta, $other) = explode('.', $id . '.');
  81. if ($field_name == $field['field_name'] && isset($delta) && $item_delta != $delta) {
  82. return $output;
  83. }
  84. }
  85. }
  86. // Check the formatter settings to see if the repeat rule should be displayed.
  87. // Show it only with the first multiple value date.
  88. list($id) = entity_extract_ids($entity_type, $entity);
  89. if (!in_array($id, $repeating_ids) && module_exists('date_repeat_field') && !empty($item['rrule']) && $options['show_repeat_rule'] == 'show') {
  90. $repeat_vars = array(
  91. 'field' => $field,
  92. 'item' => $item,
  93. 'entity_type' => $entity_type,
  94. 'entity' => $entity,
  95. );
  96. $output .= theme('date_repeat_display', $repeat_vars);
  97. $repeating_ids[] = $id;
  98. }
  99. // If this is a full node or a pseudo node created by grouping multiple
  100. // values, see exactly which values are supposed to be visible.
  101. if (isset($entity->$field_name)) {
  102. $entity = date_prepare_entity($formatter, $entity_type, $entity, $field, $instance, $langcode, $item, $display);
  103. // Did the current value get removed by formatter settings?
  104. if (empty($entity->{$field_name}[$langcode][$delta])) {
  105. return $output;
  106. }
  107. // Adjust the $element values to match the changes.
  108. $element['#entity'] = $entity;
  109. }
  110. // Verify these variables exist.
  111. if (!isset($dates['value']['formatted'])) {
  112. $dates['value']['formatted'] = '';
  113. }
  114. if (!isset($dates['value2']['formatted'])) {
  115. $dates['value2']['formatted'] = '';
  116. }
  117. if (!isset($dates['value']['formatted_time'])) {
  118. $dates['value']['formatted_time'] = '';
  119. }
  120. if (!isset($dates['value2']['formatted_time'])) {
  121. $dates['value2']['formatted_time'] = '';
  122. }
  123. if (!isset($dates['value']['formatted_timezone'])) {
  124. $dates['value']['formatted_timezone'] = '';
  125. }
  126. if (!isset($dates['value2']['formatted_timezone'])) {
  127. $dates['value2']['formatted_timezone'] = '';
  128. }
  129. switch ($options['fromto']) {
  130. case 'value':
  131. $date1 = $dates['value']['formatted'];
  132. $date2 = $date1;
  133. break;
  134. case 'value2':
  135. $date2 = $dates['value2']['formatted'];
  136. $date1 = $date2;
  137. break;
  138. default:
  139. $date1 = $dates['value']['formatted'];
  140. $date2 = $dates['value2']['formatted'];
  141. break;
  142. }
  143. // Pull the timezone, if any, out of the formatted result and tack it back on
  144. // at the end, if it is in the current formatted date.
  145. $timezone = $dates['value']['formatted_timezone'];
  146. if ($timezone) {
  147. $timezone = ' ' . $timezone;
  148. }
  149. $date1 = str_replace($timezone, '', $date1);
  150. $date2 = str_replace($timezone, '', $date2);
  151. $time1 = preg_replace('`^([\(\[])`', '', $dates['value']['formatted_time']);
  152. $time1 = preg_replace('([\)\]]$)', '', $time1);
  153. $time2 = preg_replace('`^([\(\[])`', '', $dates['value2']['formatted_time']);
  154. $time2 = preg_replace('([\)\]]$)', '', $time2);
  155. // A date with a granularity of 'hour' has a time string that is an integer
  156. // value. We can't use that to replace time strings in formatted dates.
  157. $has_time_string = date_has_time($field['settings']['granularity']);
  158. if ($precision == 'hour') {
  159. $has_time_string = FALSE;
  160. }
  161. // Check remaining days.
  162. $show_remaining_days = '';
  163. if (!empty($variables['show_remaining_days']) && isset($variables['dates']['value']['formatted_iso'])) {
  164. $remaining_days = $variables['dates']['value']['formatted_iso'];
  165. $remaining_days = floor((strtotime($remaining_days) - strtotime('now')) / (24 * 3600));
  166. // Show remaining days only for future events.
  167. if ($remaining_days >= 0) {
  168. $show_remaining_days = theme('date_display_remaining', array(
  169. 'remaining_days' => $remaining_days,
  170. ));
  171. }
  172. }
  173. // No date values, display nothing.
  174. if (empty($date1) && empty($date2)) {
  175. $output .= '';
  176. }
  177. // Start and End dates match or there is no End date, display a complete
  178. // single date.
  179. elseif ($date1 == $date2 || empty($date2)) {
  180. $output .= theme('date_display_single', array(
  181. 'date' => $date1,
  182. 'timezone' => $has_time_string ? $timezone : '',
  183. 'attributes' => $attributes,
  184. 'rdf_mapping' => $rdf_mapping,
  185. 'add_rdf' => $add_rdf,
  186. 'microdata' => $microdata,
  187. 'add_microdata' => $add_microdata,
  188. 'dates' => $dates,
  189. 'show_remaining_days' => $show_remaining_days,
  190. ));
  191. }
  192. // Same day, different times, don't repeat the date but show both Start and
  193. // End times. We can NOT do this if the replacement value is an integer
  194. // instead of a time string.
  195. elseif ($has_time_string && $dates['value']['formatted_date'] == $dates['value2']['formatted_date']) {
  196. // Replace the original time with the start/end time in the formatted start
  197. // date. Make sure that parentheses or brackets wrapping the time will be
  198. // retained in the final result.
  199. $time = theme('date_display_range', array(
  200. 'date1' => $time1,
  201. 'date2' => $time2,
  202. 'timezone' => $timezone,
  203. 'attributes' => $attributes,
  204. 'rdf_mapping' => $rdf_mapping,
  205. 'add_rdf' => $add_rdf,
  206. 'microdata' => $microdata,
  207. 'add_microdata' => $add_microdata,
  208. 'dates' => $dates,
  209. 'show_remaining_days' => $show_remaining_days,
  210. ));
  211. $replaced = str_replace($time1, $time, $date1);
  212. $output .= theme('date_display_single', array(
  213. 'date' => $replaced,
  214. 'timezone' => $timezone,
  215. 'attributes' => array(),
  216. 'rdf_mapping' => array(),
  217. 'add_rdf' => FALSE,
  218. 'dates' => $dates,
  219. ));
  220. }
  221. // Different days, display both in their entirety.
  222. else {
  223. $output .= theme('date_display_range', array(
  224. 'date1' => $date1,
  225. 'date2' => $date2,
  226. 'timezone' => $has_time_string ? $timezone : '',
  227. 'attributes' => $attributes,
  228. 'rdf_mapping' => $rdf_mapping,
  229. 'add_rdf' => $add_rdf,
  230. 'microdata' => $microdata,
  231. 'add_microdata' => $add_microdata,
  232. 'dates' => $dates,
  233. 'show_remaining_days' => $show_remaining_days,
  234. ));
  235. }
  236. return $output;
  237. }
  238. /**
  239. * Template preprocess function for displaying a single date.
  240. */
  241. function template_preprocess_date_display_single(&$variables) {
  242. if ($variables['add_rdf'] || !empty($variables['add_microdata'])) {
  243. // Pass along the rdf mapping for this field, if any. Add some default rdf
  244. // attributes that will be used if not overridden by attributes passed in.
  245. $rdf_mapping = $variables['rdf_mapping'];
  246. $base_attributes = array(
  247. 'property' => array('dc:date'),
  248. 'datatype' => 'xsd:dateTime',
  249. 'content' => $variables['dates']['value']['formatted_iso'],
  250. );
  251. $variables['attributes'] = $variables['attributes'] + $base_attributes;
  252. }
  253. // Pass along microdata attributes, or set display to FALSE if none are set.
  254. if (!empty($variables['add_microdata'])) {
  255. // Because the Entity API integration for Date has a variable data
  256. // structure depending on whether there is an end value, the attributes
  257. // could be attached to the field or to the value property.
  258. if (!empty($variables['microdata']['#attributes']['itemprop'])) {
  259. $variables['microdata']['value']['#attributes'] = $variables['microdata']['#attributes'];
  260. }
  261. // Add the machine readable time using the content attribute.
  262. if (!empty($variables['microdata']['value']['#attributes'])) {
  263. $variables['microdata']['value']['#attributes']['content'] = $variables['dates']['value']['formatted_iso'];
  264. }
  265. else {
  266. $variables['add_microdata'] = FALSE;
  267. }
  268. }
  269. }
  270. /**
  271. * Returns HTML for a date element formatted as a single date.
  272. */
  273. function theme_date_display_single($variables) {
  274. $date = $variables['date'];
  275. $timezone = $variables['timezone'];
  276. $attributes = $variables['attributes'];
  277. $attributes['class'][] = 'date-display-single';
  278. $show_remaining_days = isset($variables['show_remaining_days']) ? $variables['show_remaining_days'] : '';
  279. // Wrap the result with the attributes.
  280. $output = '<span ' . drupal_attributes($attributes) . '>' . $date . $timezone . '</span>';
  281. if (!empty($variables['add_microdata'])) {
  282. $output .= '<meta' . drupal_attributes($variables['microdata']['value']['#attributes']) . '/>';
  283. }
  284. // Add remaining message and return.
  285. return $output . $show_remaining_days;
  286. }
  287. /**
  288. * Template preprocess function for displaying a range of dates.
  289. */
  290. function template_preprocess_date_display_range(&$variables) {
  291. // Merge in the shared attributes for themes to use.
  292. $variables['attributes_start'] += $variables['attributes'];
  293. $variables['attributes_end'] += $variables['attributes'];
  294. if ($variables['add_rdf']) {
  295. // Pass along the rdf mapping for this field, if any. Add some default RDF
  296. // attributes that will be used if not overridden by attributes passed in.
  297. $dates = $variables['dates'];
  298. $base_attributes = array(
  299. 'property' => array('dc:date'),
  300. 'datatype' => 'xsd:dateTime',
  301. 'content' => $dates['value']['formatted_iso'],
  302. );
  303. $variables['attributes_start'] += $base_attributes;
  304. $variables['attributes_end'] += $base_attributes;
  305. $variables['attributes_end']['content'] = $dates['value2']['formatted_iso'];
  306. foreach ($variables['attributes_end']['property'] as $delta => $property) {
  307. $variables['attributes_end']['property'][$delta] = str_replace('start', 'end', $property);
  308. }
  309. }
  310. // Pass along microdata attributes, or set display to FALSE if none are set.
  311. if ($variables['add_microdata']) {
  312. if (!empty($variables['microdata']['value']['#attributes'])) {
  313. $variables['microdata']['value']['#attributes']['content'] = $variables['dates']['value']['formatted_iso'];
  314. $variables['microdata']['value2']['#attributes']['content'] = $variables['dates']['value2']['formatted_iso'];
  315. }
  316. else {
  317. $variables['add_microdata'] = FALSE;
  318. }
  319. }
  320. }
  321. /**
  322. * Returns HTML for a date element formatted as a range.
  323. */
  324. function theme_date_display_range($variables) {
  325. $date1 = $variables['date1'];
  326. $date2 = $variables['date2'];
  327. $timezone = $variables['timezone'];
  328. $attributes = $variables['attributes'];
  329. $attributes['class'][] = 'date-display-range';
  330. $attributes_start = $variables['attributes_start'];
  331. $attributes_start['class'][] = 'date-display-start';
  332. $attributes_end = $variables['attributes_end'];
  333. $attributes_end['class'][] = 'date-display-end';
  334. $show_remaining_days = isset($variables['show_remaining_days']) ? $variables['show_remaining_days'] : '';
  335. $start_date = '<span ' . drupal_attributes($attributes_start) . '>' . $date1 . '</span>';
  336. $end_date = '<span ' . drupal_attributes($attributes_end) . '>' . $date2 . $timezone . '</span>';
  337. // If microdata attributes for the start date property have been passed in,
  338. // add the microdata in meta tags.
  339. if (!empty($variables['add_microdata'])) {
  340. $start_date .= '<meta' . drupal_attributes($variables['microdata']['value']['#attributes']) . '/>';
  341. $end_date .= '<meta' . drupal_attributes($variables['microdata']['value2']['#attributes']) . '/>';
  342. }
  343. // Wrap the result with the attributes.
  344. $output = '<span ' . drupal_attributes($attributes) . '>' . t('!start-date to !end-date', array(
  345. '!start-date' => $start_date,
  346. '!end-date' => $end_date,
  347. )) . '</span>';
  348. // Add remaining message and return.
  349. return $output . $show_remaining_days;
  350. }
  351. /**
  352. * Returns HTML for a date element formatted as an interval.
  353. */
  354. function theme_date_display_interval($variables) {
  355. $entity = $variables['entity'];
  356. $options = $variables['display']['settings'];
  357. $dates = $variables['dates'];
  358. $attributes = $variables['attributes'];
  359. $attributes['class'][] = 'date-display-interval';
  360. // Get the formatter settings, either the default settings for this node type
  361. // or the View settings stored in $entity->date_info.
  362. if (!empty($entity->date_info) && !empty($entity->date_info->formatter_settings)) {
  363. $options = $entity->date_info->formatter_settings;
  364. }
  365. $time_ago_vars = array(
  366. 'start_date' => $dates['value']['local']['object'],
  367. 'end_date' => $dates['value2']['local']['object'],
  368. 'interval' => $options['interval'],
  369. 'interval_display' => $options['interval_display'],
  370. 'use_end_date' => !empty($options['use_end_date']) ?
  371. $options['use_end_date'] : FALSE,
  372. );
  373. if ($return = theme('date_time_ago', $time_ago_vars)) {
  374. return '<span ' . drupal_attributes($attributes) . ">$return</span>";
  375. }
  376. else {
  377. return '';
  378. }
  379. }
  380. /**
  381. * Returns HTML for a start/end date combination on form.
  382. */
  383. function theme_date_combo($variables) {
  384. $element = $variables['element'];
  385. $field = field_info_field($element['#field_name']);
  386. $instance = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
  387. // Group start/end items together in fieldset.
  388. $fieldset = array(
  389. '#title' => field_filter_xss(t($element['#title'])) . ($element['#delta'] > 0 ? ' ' . intval($element['#delta'] + 1) : ''),
  390. '#value' => '',
  391. '#description' => !empty($element['#description']) ? $element['#description'] : '',
  392. '#attributes' => array('class' => array('date-combo')),
  393. '#children' => $element['#children'],
  394. );
  395. // Add marker to required date fields.
  396. if ($element['#required']) {
  397. $fieldset['#title'] .= " " . theme('form_required_marker');
  398. }
  399. return theme('fieldset', array('element' => $fieldset));
  400. }
  401. /**
  402. * Returns HTML for the text/select options for date parts in a table.
  403. */
  404. function theme_date_text_parts($variables) {
  405. $element = $variables['element'];
  406. $rows = array();
  407. foreach (date_granularity_names() as $key => $part) {
  408. if ($element[$key]['#type'] == 'hidden') {
  409. $rows[] = drupal_render($element[$key]);
  410. }
  411. else {
  412. $rows[] = array(
  413. $part,
  414. drupal_render($element[$key][0]),
  415. drupal_render($element[$key][1]),
  416. );
  417. }
  418. }
  419. if ($element['year']['#type'] == 'hidden') {
  420. return implode($rows) . drupal_render_children($element);
  421. }
  422. else {
  423. $header = array(t('Date part'), t('Select list'), t('Text field'));
  424. return theme('table', array('header' => $header, 'rows' => $rows)) . drupal_render_children($element);
  425. }
  426. }
  427. /**
  428. * Render a date combo as a form element.
  429. */
  430. function theme_date_form_element($variables) {
  431. $element = &$variables['element'];
  432. // Detect whether element is multiline.
  433. $count = preg_match_all('`<(?:div|span)\b[^>]* class="[^"]*\b(?:date-no-float|date-clear)\b`', $element['#children'], $matches, PREG_OFFSET_CAPTURE);
  434. $multiline = FALSE;
  435. if ($count > 1) {
  436. $multiline = TRUE;
  437. }
  438. elseif ($count) {
  439. $before = substr($element['#children'], 0, $matches[0][0][1]);
  440. if (preg_match('`<(?:div|span)\b[^>]* class="[^"]*\bdate-float\b`', $before)) {
  441. $multiline = TRUE;
  442. }
  443. }
  444. // Detect if there is more than one subfield.
  445. $count = count(explode('<label', $element['#children'])) - 1;
  446. if ($count == 1) {
  447. // If there is only one subfield, hide the label of the main field only if
  448. // the label of the subfield is visible.
  449. if (empty($variables['element']['value']['#instance'])
  450. || empty($variables['element']['value']['#instance']['widget'])
  451. || empty($variables['element']['value']['#instance']['widget']['settings'])
  452. || empty($variables['element']['value']['#instance']['widget']['settings']['label_position'])
  453. || $variables['element']['value']['#instance']['widget']['settings']['label_position'] != 'none') {
  454. $element['#title_display'] = 'none';
  455. }
  456. }
  457. // Wrap children with a div and add an extra class if element is multiline.
  458. $element['#children'] = '<div class="date-form-element-content' . ($multiline ? ' date-form-element-content-multiline' : '') . '">' . $element['#children'] . '</div>';
  459. return theme('form_element', $variables);
  460. }
  461. /**
  462. * Returns HTML for remaining message.
  463. */
  464. function theme_date_display_remaining($variables) {
  465. $remaining_days = $variables['remaining_days'];
  466. $output = '';
  467. $show_remaining_text = t('Less than 1 day remaining');
  468. if ($remaining_days) {
  469. $show_remaining_text = format_plural($remaining_days, '1 day remaining', '@count days remaining');
  470. }
  471. return '<div class="date-display-remaining"><span class="date-display-remaining">' . $show_remaining_text . '</span></div>';
  472. }
  473. /**
  474. * @} End of addtogroup themeable */