timefield.module 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475
  1. <?php
  2. /**
  3. * @file
  4. * Defines a Field API field for time
  5. */
  6. /**
  7. * Implements hook_field_info().
  8. */
  9. function timefield_field_info() {
  10. $fields = array();
  11. $fields['timefield'] = array(
  12. 'label' => t('Time Field'),
  13. 'description' => t('A field type for storing times, with an optional end time and weekday repeat schedule.'),
  14. 'settings' => array(
  15. 'totime' => '',
  16. 'weekly_summary' => FALSE,
  17. 'weekly_summary_with_label' => FALSE,
  18. ),
  19. 'instance_settings' => array(
  20. 'disable_plugin' => FALSE,
  21. 'input_format' => array(
  22. 'separator' => ':',
  23. 'showLeadingZero' => FALSE,
  24. 'showMinutesLeadingZero' => TRUE,
  25. 'showPeriod' => TRUE,
  26. 'periodSeparator' => '',
  27. 'showHours' => TRUE,
  28. 'showMinutes' => TRUE,
  29. 'am_text' => 'AM',
  30. 'pm_text' => 'PM',
  31. 'minute_interval' => 5,
  32. 'showCloseButton' => FALSE,
  33. 'closeButtonText' => 'Done',
  34. 'showNowButton' => FALSE,
  35. 'nowButtonText' => 'Now',
  36. 'showDeselectButton' => FALSE,
  37. 'deselectButtonText' => 'Deselect',
  38. 'myPosition' => 'left top',
  39. 'atPosition' => 'left bottom',
  40. ),
  41. ),
  42. 'default_widget' => 'timefield_standard',
  43. 'default_formatter' => 'timefield_default',
  44. 'property_type' => 'timefield',
  45. 'property_callbacks' => array('timefield_property_info_callback'),
  46. );
  47. return $fields;
  48. }
  49. /**
  50. * Implements hook_library().
  51. */
  52. function timefield_library() {
  53. $libraries = array();
  54. $libs = libraries_get_libraries();
  55. if (array_key_exists('jquery.timepicker', $libs)) {
  56. $path = $libs['jquery.timepicker'];
  57. $libraries['timepicker'] = array(
  58. 'title' => 'Timepicker',
  59. 'website' => 'http://fgelinas.com/code/timepicker',
  60. 'version' => '7.x',
  61. 'js' => array($path . '/jquery.ui.timepicker.js' => array()),
  62. 'css' => array($path . '/jquery.ui.timepicker.css' => array()),
  63. 'dependencies' => array(
  64. array('system', 'ui'),
  65. array('system', 'ui.position'),
  66. ),
  67. );
  68. }
  69. return $libraries;
  70. }
  71. /**
  72. * Implements hook_theme().
  73. */
  74. function timefield_theme() {
  75. return array(
  76. 'timefield_formatter' => array(
  77. 'variables' => array('time' => NULL, 'settings' => NULL, 'format' => NULL),
  78. 'template' => 'timefield',
  79. 'path' => drupal_get_path('module', 'timefield') . '/theme',
  80. ),
  81. 'timefield_weekly_summary_minical_box' => array(
  82. 'render element' => 'element',
  83. 'template' => 'timefield-weekly-minical-box',
  84. 'path' => drupal_get_path('module', 'timefield') . '/theme',
  85. ),
  86. );
  87. }
  88. /**
  89. * Implements hook_field_settings_form().
  90. */
  91. function timefield_field_settings_form($field, $instance, $has_data) {
  92. $settings = $field['settings'];
  93. $form['totime'] = array(
  94. '#type' => 'select',
  95. '#title' => t('To Time'),
  96. '#options' => array('' => t('Never'), 'optional' => t('Optional'), 'required' => t('Required')),
  97. '#description' => t('Whether this field should include an end time.'),
  98. '#default_value' => $settings['totime'],
  99. '#disabled' => $has_data,
  100. );
  101. $form['weekly_summary'] = array(
  102. '#type' => 'checkbox',
  103. '#title' => t('Add Weekly Repeat Checkboxes'),
  104. '#description' => t('Should this field include options to specify the days on which it repeats.'),
  105. '#default_value' => $settings['weekly_summary'],
  106. '#disabled' => $has_data,
  107. );
  108. $form['weekly_summary_with_label'] = array(
  109. '#type' => 'checkbox',
  110. '#title' => t('Add Weekly Repeat Checkboxes with Label for each Time'),
  111. '#description' => t('Same as above with an additional label field for describing times.'),
  112. '#default_value' => $settings['weekly_summary_with_label'],
  113. '#disabled' => $has_data,
  114. );
  115. return $form;
  116. }
  117. /**
  118. * Implements hook_field_instance_settings_form().
  119. */
  120. function timefield_field_instance_settings_form($field, $instance) {
  121. $settings = $instance['settings'];
  122. $form['disable_plugin'] = array(
  123. '#title' => t('Disable jQuery Timepicker plugin.'),
  124. '#type' => 'checkbox',
  125. '#default_value' => isset($settings['disable_plugin']) ? $settings['disable_plugin'] : FALSE,
  126. '#description' => t('Do not use jQuery Timepicker plugin for input.'),
  127. );
  128. $form['input_format'] = array(
  129. '#title' => t('Time Input Format'),
  130. '#type' => 'fieldset',
  131. );
  132. $form['input_format']['separator'] = array(
  133. '#title' => t('Hour and Minute Separator'),
  134. '#type' => 'textfield',
  135. '#default_value' => isset($settings['input_format']['separator']) ? $settings['input_format']['separator'] : ':',
  136. '#size' => 10,
  137. '#description' => t('The character to use to separate hours and minutes.'),
  138. );
  139. $form['input_format']['showLeadingZero'] = array(
  140. '#title' => t('Show Leading Zero for Hour'),
  141. '#type' => 'checkbox',
  142. '#default_value' => isset($settings['input_format']['showLeadingZero']) ? $settings['input_format']['showLeadingZero'] : FALSE,
  143. '#description' => t('Whether or not to show a leading zero for hours < 10.'),
  144. );
  145. $form['input_format']['showPeriod'] = array(
  146. '#title' => t('Show AM/PM Label'),
  147. '#type' => 'checkbox',
  148. '#default_value' => isset($settings['input_format']['showPeriod']) ? $settings['input_format']['showPeriod'] : FALSE,
  149. '#description' => t('Whether or not to show AM/PM on the input textfield both on the widget and in the text field after selecting the time with the widget.'),
  150. );
  151. $form['input_format']['periodSeparator'] = array(
  152. '#title' => t('What character should appear between the time and the Period (AM/PM)'),
  153. '#type' => 'textfield',
  154. '#default_value' => isset($settings['input_format']['periodSeparator']) ? $settings['input_format']['periodSeparator'] : '',
  155. '#size' => 10,
  156. '#description' => t('The character to use to separate the time from the time period (AM/PM).'),
  157. );
  158. $form['input_format']['am_text'] = array(
  159. '#title' => t('AM text'),
  160. '#type' => 'textfield',
  161. '#default_value' => isset($settings['input_format']['am_text']) ? $settings['input_format']['am_text'] : 'AM',
  162. '#size' => 10,
  163. );
  164. $form['input_format']['pm_text'] = array(
  165. '#title' => t('PM text'),
  166. '#type' => 'textfield',
  167. '#default_value' => isset($settings['input_format']['pm_text']) ? $settings['input_format']['pm_text'] : 'PM',
  168. '#size' => 10,
  169. );
  170. $form['input_format']['showCloseButton'] = array(
  171. '#title' => t('Show a Button to Close the Picker Widget'),
  172. '#type' => 'checkbox',
  173. '#default_value' => isset($settings['input_format']['showCloseButton']) ? $settings['input_format']['showCloseButton'] : FALSE,
  174. '#states' => array(
  175. 'invisible' => array(
  176. ':input[name="instance[settings][disable_plugin]"]' => array('checked' => TRUE),
  177. ),
  178. ),
  179. );
  180. $form['input_format']['closeButtonText'] = array(
  181. '#title' => t('Close Button text'),
  182. '#type' => 'textfield',
  183. '#default_value' => isset($settings['input_format']['closeButtonText']) ? $settings['input_format']['closeButtonText'] : 'Close',
  184. '#size' => 10,
  185. '#states' => array(
  186. 'invisible' => array(
  187. ':input[name="instance[settings][disable_plugin]"]' => array('checked' => TRUE),
  188. ),
  189. ),
  190. );
  191. $form['input_format']['showNowButton'] = array(
  192. '#title' => t('Show a Button to Select the Current Time'),
  193. '#type' => 'checkbox',
  194. '#default_value' => isset($settings['input_format']['showNowButton']) ? $settings['input_format']['showNowButton'] : FALSE,
  195. '#states' => array(
  196. 'invisible' => array(
  197. ':input[name="instance[settings][disable_plugin]"]' => array('checked' => TRUE),
  198. ),
  199. ),
  200. );
  201. $form['input_format']['nowButtonText'] = array(
  202. '#title' => t('Now Button text'),
  203. '#type' => 'textfield',
  204. '#default_value' => isset($settings['input_format']['nowButtonText']) ? $settings['input_format']['nowButtonText'] : 'Now',
  205. '#size' => 10,
  206. '#states' => array(
  207. 'invisible' => array(
  208. ':input[name="instance[settings][disable_plugin]"]' => array('checked' => TRUE),
  209. ),
  210. ),
  211. );
  212. $form['input_format']['showDeselectButton'] = array(
  213. '#title' => t('Show a Button to Deselect the time in the Picker Widget'),
  214. '#type' => 'checkbox',
  215. '#default_value' => isset($settings['input_format']['showDeselectButton']) ? $settings['input_format']['showDeselectButton'] : FALSE,
  216. '#states' => array(
  217. 'invisible' => array(
  218. ':input[name="instance[settings][disable_plugin]"]' => array('checked' => TRUE),
  219. ),
  220. ),
  221. );
  222. $form['input_format']['deselectButtonText'] = array(
  223. '#title' => t('Deselect Button text'),
  224. '#type' => 'textfield',
  225. '#default_value' => isset($settings['input_format']['deselectButtonText']) ? $settings['input_format']['deselectButtonText'] : 'Deselect',
  226. '#size' => 10,
  227. '#states' => array(
  228. 'invisible' => array(
  229. ':input[name="instance[settings][disable_plugin]"]' => array('checked' => TRUE),
  230. ),
  231. ),
  232. );
  233. $form['input_format']['myPosition'] = array(
  234. '#title' => t('my Position'),
  235. '#type' => 'select',
  236. '#default_value' => isset($settings['input_format']['myPosition']) ? $settings['input_format']['myPosition'] : 'left top',
  237. '#options' => drupal_map_assoc(array('left top', 'left center', 'left bottom', 'center top', 'center center', 'center bottom', 'right top', 'right center', 'right bottom')),
  238. '#description' => t('Corner of the timpicker widget dialog to position. See !jquery_info for more info.', array('!jquery_info' => l(t("jQuery UI Position documentation"), 'http://jqueryui.com/demos/position', array('absolute' => TRUE)))),
  239. '#states' => array(
  240. 'invisible' => array(
  241. ':input[name="instance[settings][disable_plugin]"]' => array('checked' => TRUE),
  242. ),
  243. ),
  244. );
  245. $form['input_format']['atPosition'] = array(
  246. '#title' => t('at Position'),
  247. '#type' => 'select',
  248. '#options' => drupal_map_assoc(array('left top', 'left center', 'left bottom', 'center top', 'center center', 'center bottom', 'right top', 'right center', 'right bottom')),
  249. '#default_value' => isset($settings['input_format']['atPosition']) ? $settings['input_format']['atPosition'] : 'left bottom',
  250. '#description' => t('Where to position "my Position" relative to input widget textfield See !jquery_info for more info.', array('!jquery_info' => l(t("jQuery UI Position documentation"), 'http://jqueryui.com/demos/position', array('absolute' => TRUE)))),
  251. '#states' => array(
  252. 'invisible' => array(
  253. ':input[name="instance[settings][disable_plugin]"]' => array('checked' => TRUE),
  254. ),
  255. ),
  256. );
  257. return $form;
  258. }
  259. /**
  260. * Implements hook_field_widget_info().
  261. */
  262. function timefield_field_widget_info() {
  263. $widgets = array();
  264. $widgets['timefield_standard'] = array(
  265. 'label' => t('Time Field'),
  266. 'description' => t('Input Form for a Time Field'),
  267. 'field types' => array('timefield'),
  268. 'settings' => array(
  269. 'label_position' => 'above',
  270. ),
  271. );
  272. return $widgets;
  273. }
  274. /**
  275. * Implements hook_field_formatter_info().
  276. */
  277. function timefield_field_formatter_info() {
  278. return array(
  279. 'timefield_default' => array(
  280. 'label' => t('List of Times'),
  281. 'field types' => array('timefield'),
  282. 'settings' => array(
  283. 'display_format' => array(
  284. 'separator' => ':',
  285. 'period_separator' => '',
  286. 'period' => 'a',
  287. 'hour' => 'g',
  288. 'minute' => 'i',
  289. ),
  290. ),
  291. ),
  292. 'timefield_duration' => array(
  293. 'label' => t('Duration'),
  294. 'field types' => array('timefield'),
  295. 'settings' => array(
  296. 'duration_format' => 'minutes',
  297. 'display_format' => array(
  298. 'separator' => ':',
  299. 'period_separator' => '',
  300. 'period' => 'a',
  301. 'hour' => 'g',
  302. 'minute' => 'i',
  303. ),
  304. ),
  305. ),
  306. 'timefield_weekly_summary_minical' => array(
  307. 'label' => t('Mini Calendar'),
  308. 'field types' => array('timefield'),
  309. 'settings' => array(
  310. 'display_format' => array(
  311. 'separator' => ':',
  312. 'period_separator' => '',
  313. 'period' => 'a',
  314. 'hour' => 'g',
  315. 'minute' => 'i',
  316. ),
  317. 'column_format' => array(
  318. 'separator' => ':',
  319. 'period_separator' => '',
  320. 'period' => 'a',
  321. 'hour' => 'g',
  322. 'minute' => 'i',
  323. ),
  324. 'first_day' => 'mon',
  325. 'absolute_start' => '8:00 am',
  326. 'absolute_end' => '10:00 pm',
  327. 'range' => 120,
  328. 'adjust_range' => FALSE,
  329. ),
  330. ),
  331. );
  332. }
  333. /**
  334. * Implements hook_field_formatter_settings_form().
  335. */
  336. function timefield_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  337. $display = $instance['display'][$view_mode];
  338. $settings = $display['settings'];
  339. $instance_settings = $instance['settings'];
  340. $element = array();
  341. $instance_class = str_replace('_', '-', $instance['field_name']);
  342. if ($display['type'] == 'timefield_duration') {
  343. $element['duration_format'] = array(
  344. '#title' => t('Time Duration Format'),
  345. '#type' => 'select',
  346. '#options' => _timefield_duration_options(),
  347. '#default_value' => $settings['duration_format'],
  348. '#required' => TRUE,
  349. );
  350. $element += _timefield_display_format_form('display_format', "Time Input Settings", $settings);
  351. }
  352. if ($display['type'] == 'timefield_default') {
  353. }
  354. if ($display['type'] == 'timefield_weekly_summary_minical') {
  355. $js_settings = _timefield_js_settings($instance_class, $instance_settings['input_format']);
  356. $element['#attached'] = array(
  357. 'library' => array(
  358. array('timefield', 'timepicker'),
  359. ),
  360. 'js' => array(
  361. drupal_get_path('module', 'timefield') . '/js/timefield.js',
  362. array(
  363. 'data' => array('timefield' => $js_settings),
  364. 'type' => 'setting',
  365. ),
  366. ),
  367. );
  368. $element += _timefield_display_format_form('column_format', "Column Time Settings", $settings);
  369. $element['first_day'] = array(
  370. '#title' => t('First Day of the Week'),
  371. '#type' => 'select',
  372. '#options' => _timefield_weekly_summary_days(),
  373. '#default_value' => $settings['first_day'],
  374. '#required' => TRUE,
  375. );
  376. $element['absolute_start'] = array(
  377. '#title' => t('Mini Cal Start Time'),
  378. '#description' => t('The Start Time of the Calendar'),
  379. '#type' => 'textfield',
  380. '#default_value' => $settings['absolute_start'],
  381. '#size' => 15,
  382. '#maxlength' => 15,
  383. '#attributes' => array('class' => array('edit-timefield-timepicker', $instance_class)),
  384. );
  385. $element['absolute_end'] = array(
  386. '#title' => t('Mini Cal End Time'),
  387. '#description' => t('The End Time of the Calendar'),
  388. '#type' => 'textfield',
  389. '#default_value' => $settings['absolute_end'],
  390. '#size' => 15,
  391. '#maxlength' => 15,
  392. '#attributes' => array('class' => array('edit-timefield-timepicker', $instance_class)),
  393. );
  394. $element['range'] = array(
  395. '#type' => 'select',
  396. '#title' => t('Select the time duration of each block of time'),
  397. '#options' => array(30 => '30 Minutes', 60 => '1 Hour', 90 => '90 minutes', 120 => '2 Hours', 180 => '3 Hours', 240 => '4 Hours'),
  398. '#default_value' => $settings['range'],
  399. );
  400. $element['adjust_range'] = array(
  401. '#type' => 'checkbox',
  402. '#title' => t('Adjust Range to fit Items'),
  403. '#default_value' => $settings['adjust_time'],
  404. );
  405. }
  406. if ($display['type'] == 'timefield_weekly_summary_minical' || $display['type'] == 'timefield_default') {
  407. $element += _timefield_display_format_form('display_format', "Individual Time Display Settings", $settings);
  408. }
  409. return $element;
  410. }
  411. /**
  412. * Helper Function to build settings form
  413. *
  414. * @param string $name
  415. * The name of the element array
  416. * @param string $fieldset_header
  417. * The title for the fieldset
  418. * @param array $settings
  419. * The settings parameters
  420. *
  421. * @return array
  422. * $element
  423. * Form Element
  424. */
  425. function _timefield_display_format_form($name, $fieldset_header, $settings) {
  426. $element[$name] = array(
  427. '#title' => check_plain($fieldset_header),
  428. '#type' => 'fieldset',
  429. );
  430. $element[$name]['hour'] = array(
  431. '#title' => t('Hour Format'),
  432. '#type' => 'select',
  433. '#default_value' => isset($settings[$name]['hour']) ? $settings[$name]['hour'] : 'g',
  434. '#options' => _timefield_time_part_format('hour'),
  435. );
  436. $element[$name]['minute'] = array(
  437. '#title' => t('Minute Format'),
  438. '#type' => 'select',
  439. '#default_value' => isset($settings[$name]['minute']) ? $settings[$name]['minute'] : 'i',
  440. '#options' => _timefield_time_part_format('minute'),
  441. );
  442. $element[$name]['separator'] = array(
  443. '#title' => t('Hour and Minute Separator'),
  444. '#type' => 'textfield',
  445. '#default_value' => isset($settings[$name]['separator']) ? $settings[$name]['separator'] : ':',
  446. '#size' => 10,
  447. );
  448. $element[$name]['period'] = array(
  449. '#title' => t('AM/PM format'),
  450. '#type' => 'select',
  451. '#default_value' => isset($settings[$name]['period']) ? $settings[$name]['period'] : 'a',
  452. '#options' => _timefield_time_part_format('period'),
  453. );
  454. $element[$name]['period_separator'] = array(
  455. '#title' => t('Minute and period separtor'),
  456. '#type' => 'textfield',
  457. '#default_value' => isset($settings[$name]['period_separator']) ? $settings[$name]['period_separator'] : '',
  458. '#size' => 10,
  459. '#description' => t('The character used to separate the time from the time period (AM/PM)'),
  460. );
  461. return $element;
  462. }
  463. /**
  464. * Implements hook_field_formatter_settings_summary().
  465. */
  466. function timefield_field_formatter_settings_summary($field, $instance, $view_mode) {
  467. $display = $instance['display'][$view_mode];
  468. $settings = $display['settings'];
  469. $field_settings = $field['settings'];
  470. if (($display['type']) == 'timefield_default') {
  471. $current_time = timefield_time_to_integer(date('g:ia', strtotime("now")));
  472. return t('Current Format') . ': ' . timefield_integer_to_time($settings['display_format'], $current_time);
  473. }
  474. elseif (($display['type']) == 'timefield_duration') {
  475. if (empty($field_settings['totime'])) {
  476. return t('This display format is invalid for the current field configuration');
  477. }
  478. else {
  479. return t('Current Format') . ': ' . _timefield_duration_options($settings['duration_format']);
  480. }
  481. }
  482. elseif (($display['type']) == 'timefield_weekly_summary_minical') {
  483. if (!$field_settings['weekly_summary'] || !$field_settings['weekly_summary_with_label']) {
  484. return t('This display format is invalid for the current field configuration');
  485. }
  486. else {
  487. return t('Current Format: Mini Calendar Format, expand to see current Selection');
  488. }
  489. }
  490. }
  491. /**
  492. * Element validation function
  493. */
  494. function timefield_time_validate($element, &$form_state, $form) {
  495. $delta = $element['#delta'];
  496. $field_name = $element['#field_name'];
  497. if ($field_value = drupal_array_get_nested_value($form_state['values'], $element['#field_parents'])) {
  498. $field_parent = drupal_array_get_nested_value($form, $element['#field_parents']);
  499. if (isset($field_parent[$field_name])) {
  500. $field = $field_parent[$field_name][$element['#language']][$delta];
  501. }
  502. else {
  503. $field = $field_parent['#field'];
  504. }
  505. $values = $field_value[$field_name][$element['#language']][$delta];
  506. $new_values = array();
  507. // If empty, set to null.
  508. if (strlen($values['value']) == 0) {
  509. if ($field && isset($field['#required']) && $field['#required']) {
  510. form_error($element['value'], t('!name field is required.', array('!name' => $element['#title'])));
  511. }
  512. form_set_value($element, array('value' => NULL), $form_state);
  513. return;
  514. }
  515. $date_value = date_parse($values['value']);
  516. if ($date_value['error_count']) {
  517. form_error($element['value'], t('The time is not in a format that I understand.'));
  518. }
  519. else {
  520. $parsed_value = timefield_time_to_integer($values['value']);
  521. $new_values['value'] = $parsed_value;
  522. }
  523. if (!empty($values['value2'])) {
  524. $date_value2 = date_parse($values['value2']);
  525. if ($date_value2['error_count']) {
  526. form_error($element['value2'], t('The to time is not in a format that I understand.'));
  527. }
  528. else {
  529. $parsed_value = timefield_time_to_integer($values['value2']);
  530. if ($values['value'] > $parsed_value || $parsed_value == 0) {
  531. $parsed_value += 86400;
  532. }
  533. $new_values['value2'] = $parsed_value;
  534. }
  535. }
  536. if (!empty($values['days'])) {
  537. foreach ($values['days'] as $index => $day) {
  538. $new_values[$index] = ($day === 0) ? $day : 1;
  539. }
  540. }
  541. if (!empty($values['label'])) {
  542. $new_values['label'] = isset($values['label']) ? $values['label'] : '';
  543. }
  544. form_set_value($element, $new_values, $form_state);
  545. }
  546. }
  547. /**
  548. * Implements hook_field_validate().
  549. */
  550. function timefield_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
  551. foreach ($items as $delta => $item) {
  552. if (!timefield_field_is_empty($item, array())) {
  553. if (!is_numeric($item['value'])) {
  554. $errors[$field['field_name']][$langcode][$delta][] = array(
  555. 'error' => 'timefield_invalid_value',
  556. 'message' => t('Time values are incorrect.'),
  557. );
  558. }
  559. // If field instance requires an end time, check to see that is set
  560. if ($field['settings']['totime'] == 'required') {
  561. if (!isset($item['value2']) || timefield_is_empty_value($item['value2'])) {
  562. $errors[$field['field_name']][$langcode][$delta][] = array(
  563. 'error' => 'timefield_invalid_value2',
  564. 'message' => t('An End Time Is required.'),
  565. );
  566. }
  567. }
  568. }
  569. if (isset($item['value2']) && !timefield_is_empty_value($item['value2'])) {
  570. if (!is_numeric($item['value2'])) {
  571. $errors[$field['field_name']][$langcode][$delta][] = array(
  572. 'error' => 'timefield_invalid_value2',
  573. 'message' => t('Time values are incorrect.'),
  574. );
  575. }
  576. }
  577. }
  578. }
  579. /**
  580. * Implements hook_field_widget_error().
  581. */
  582. function timefield_field_widget_error($element, $error, $form, &$form_state) {
  583. switch ($error['error']) {
  584. case 'timefield_invalid_value':
  585. form_error($element['value'], $error['message']);
  586. break;
  587. case 'timefield_invalid_value2':
  588. form_error($element['value2'], $error['message']);
  589. break;
  590. default:
  591. form_error($element, $error['message']);
  592. break;
  593. }
  594. }
  595. /**
  596. * Implements hook_field_is_empty().
  597. */
  598. function timefield_field_is_empty($item, $field) {
  599. // Every timefield must have at least single time value or it is considered
  600. // empty.
  601. if (!isset($item['value'])) {
  602. return TRUE;
  603. }
  604. return timefield_is_empty_value($item['value']);
  605. }
  606. /**
  607. * Helper function to determine if a field is empty
  608. */
  609. function timefield_is_empty_value($value) {
  610. return !isset($value) || (empty($value) && $value !== 0 && $value !== '0');
  611. }
  612. /**
  613. * Implements hook_field_formatter_view().
  614. */
  615. function timefield_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  616. $settings = $display['settings'];
  617. $settings += $field['settings'];
  618. $element = array();
  619. switch ($display['type']) {
  620. case 'timefield_default':
  621. foreach ($items as $delta => $item) {
  622. $element[$delta] = array(
  623. '#theme' => array('timefield_formatter'),
  624. '#time' => $item,
  625. '#settings' => $settings,
  626. '#format' => 'default',
  627. );
  628. }
  629. break;
  630. case 'timefield_duration':
  631. foreach ($items as $delta => $item) {
  632. $element[$delta] = array(
  633. '#theme' => array('timefield_formatter'),
  634. '#time' => $item,
  635. '#settings' => $settings,
  636. '#format' => 'duration',
  637. );
  638. }
  639. break;
  640. case 'timefield_weekly_summary_minical':
  641. $header = _timefield_weekly_summary_build_header($settings['first_day']);
  642. $rows = timefield_weekly_summary_build_rows($items, $header, $settings);
  643. $element[0] = array(
  644. '#header' => $header,
  645. '#rows' => $rows,
  646. '#theme' => 'table',
  647. );
  648. break;
  649. }
  650. return $element;
  651. }
  652. /**
  653. * Implements hook_field_widget_form().
  654. */
  655. function timefield_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  656. $settings = $field['settings'];
  657. $instance_settings = $instance['settings'];
  658. $display_settings = $instance['display']['default']['settings'];
  659. // If the element is hidden, it might not have display settings, so we just
  660. // get the defaults @see #1862316
  661. if (empty($display_settings)) {
  662. $formatter_settings = timefield_field_formatter_info();
  663. $display_settings = $formatter_settings['timefield_default']['settings'];
  664. }
  665. $element += array(
  666. '#delta' => $delta,
  667. );
  668. $instance_class = str_replace('_', '-', $element['#field_name']) . "-" . $delta;
  669. $value = isset($items[$delta]['value']) ? timefield_integer_to_time($display_settings['display_format'], $items[$delta]['value']) : '';
  670. if ($settings['weekly_summary'] || $settings['weekly_summary_with_label']) {
  671. $days = isset($items[$delta]['mon']) ? _timefield_weekly_summary_days_map($items[$delta]) : array();
  672. }
  673. if ($settings['totime'] == 'required' || $settings['totime'] == 'optional') {
  674. $value2 = isset($items[$delta]['value2']) ? timefield_integer_to_time($display_settings['display_format'], $items[$delta]['value2']) : '';
  675. }
  676. switch ($instance['widget']['type']) {
  677. case 'timefield_standard':
  678. $element += array(
  679. '#title' => 'Time',
  680. '#type' => 'fieldset',
  681. '#delta' => $delta,
  682. '#element_validate' => array('timefield_time_validate'),
  683. );
  684. if (!$instance_settings['disable_plugin']) {
  685. $js_settings = _timefield_js_settings($instance_class, $instance_settings['input_format']);
  686. $element['#attached'] = array(
  687. 'library' => array(array('timefield', 'timepicker')),
  688. 'js' => array(
  689. drupal_get_path('module', 'timefield') . '/js/timefield.js',
  690. array(
  691. 'data' => array('timefield' => $js_settings),
  692. 'type' => 'setting',
  693. )
  694. ),
  695. );
  696. }
  697. if ($settings['weekly_summary_with_label']) {
  698. $element['label'] = array(
  699. '#title' => t('Label'),
  700. '#description' => t('Enter a label for the summary'),
  701. '#type' => 'textfield',
  702. '#default_value' => isset($items[$delta]['label']) ? $items[$delta]['label'] : '',
  703. '#size' => 40,
  704. '#maxlength' => 60,
  705. );
  706. }
  707. $element['value'] = array(
  708. '#title' => t('Time'),
  709. '#description' => t('Enter a time value, in any format'),
  710. '#type' => 'textfield',
  711. '#default_value' => _timefield_map_input_format_to_display_format($value, $instance_settings['input_format']),
  712. '#size' => 15,
  713. '#maxlength' => 15,
  714. '#attributes' => array('class' => array('edit-timefield-timepicker', $instance_class)),
  715. );
  716. // Add second element if totime is required or optional.
  717. if ($settings['totime'] == 'required' || $settings['totime'] == 'optional') {
  718. $element['value2'] = array(
  719. '#title' => t('End Time'),
  720. '#description' => t('Enter a time value, in any format'),
  721. '#type' => 'textfield',
  722. '#default_value' => _timefield_map_input_format_to_display_format($value2, $instance_settings['input_format']),
  723. '#size' => 15,
  724. '#maxlength' => 15,
  725. '#attributes' => array('class' => array('edit-timefield-timepicker', $instance_class)),
  726. );
  727. }
  728. if ($settings['weekly_summary'] || $settings['weekly_summary_with_label']) {
  729. $element['days'] = array(
  730. '#title' => t('Days'),
  731. '#description' => t('Select the days this schedule applies to'),
  732. '#type' => 'checkboxes',
  733. '#options' => _timefield_weekly_summary_days(),
  734. '#default_value' => $days,
  735. '#attributes' => array('class' => array('edit-field-timefield-days')),
  736. );
  737. }
  738. break;
  739. }
  740. return $element;
  741. }
  742. /**
  743. * Implements hook_field_load().
  744. *
  745. * Where possible, generate the formatted version of each field early so that
  746. * it is cached in the field cache.
  747. *
  748. * Also we do this so the entity property callbacks can return intelligible
  749. * results.
  750. *
  751. */
  752. function timefield_field_load($entity_type, $entities, $field, $instances, $langcode, &$items) {
  753. foreach ($entities as $id => $entity) {
  754. foreach ($items[$id] as $delta => $item) {
  755. if ($field['type'] == 'timefield' && isset($instances[$id]['display']['default']['settings'])) {
  756. $display_settings = $instances[$id]['display']['default']['settings'];
  757. // If the element is hidden, it might not have display settings, so we just
  758. // get the defaults @see #1862316
  759. if (!isset($display_settings['display_format'])) {
  760. $formatter_settings = timefield_field_formatter_info();
  761. $display_settings = $formatter_settings['timefield_default']['settings']['display_format'];
  762. }
  763. else {
  764. $display_settings = $display_settings['display_format'];
  765. }
  766. $items[$id][$delta]['value_formatted'] = isset($item['value']) ? timefield_integer_to_time($display_settings, $item['value']) : '';
  767. $items[$id][$delta]['value2_formatted'] = isset($item['value2']) ? timefield_integer_to_time($display_settings, $item['value2']) : '';
  768. }
  769. }
  770. }
  771. }
  772. /**
  773. * Preprocess function for the timefield formatter.
  774. */
  775. function template_preprocess_timefield_formatter(&$variables) {
  776. if ($variables['format'] == 'default') {
  777. // Encode the time elements.
  778. $variables['time']['value'] = check_plain($variables['time']['value']);
  779. $variables['time']['formatted_value'] = trim(timefield_integer_to_time($variables['settings']['display_format'], $variables['time']['value']));
  780. $variables['time']['time'] = $variables['time']['formatted_value'];
  781. if (isset($variables['time']['value2'])) {
  782. $variables['time']['value2'] = check_plain($variables['time']['value2']);
  783. $variables['time']['formatted_value2'] = trim(timefield_integer_to_time($variables['settings']['display_format'], $variables['time']['value2']));
  784. $variables['time']['time'] .= ' - ' . $variables['time']['formatted_value2'];
  785. }
  786. if ($variables['settings']['weekly_summary'] || $variables['settings']['weekly_summary_with_label']) {
  787. foreach (_timefield_weekly_summary_days() as $day => $day_text) {
  788. if ((bool) $variables['time'][$day]) {
  789. $days[$day] = $day_text;
  790. }
  791. }
  792. if ($days) {
  793. $variables['time']['days'] = $days;
  794. $variables['time']['time'] = implode(', ', $days) . ' ' . $variables['time']['time'];
  795. }
  796. }
  797. }
  798. elseif ($variables['format'] == 'duration') {
  799. // Encode the time elements.
  800. $variables['time']['value'] = check_plain($variables['time']['value']);
  801. $variables['time']['formatted_value'] = trim(timefield_integer_to_time($variables['settings']['display_format'], $variables['time']['value']));
  802. if (isset($variables['time']['value2'])) {
  803. $variables['time']['value2'] = check_plain($variables['time']['value2']);
  804. $variables['time']['formatted_value2'] = trim(timefield_integer_to_time($variables['settings']['display_format'], $variables['time']['value2']));
  805. $variables['time']['duration'] = timefield_time_to_duration($variables['time']['value'], $variables['time']['value2'], $variables['settings']['duration_format']);
  806. $variables['time']['time'] = timefield_time_to_duration($variables['time']['value'], $variables['time']['value2'], $variables['settings']['duration_format']);
  807. }
  808. else {
  809. $variables['time']['time'] = 0;
  810. }
  811. }
  812. }
  813. /**
  814. * Template preprocess function for the minical box. Every element added to a
  815. * minical will be themed with this function.
  816. */
  817. function template_preprocess_timefield_weekly_summary_minical_box(&$variables) {
  818. $variables['label'] = $variables['element']['label'];
  819. $variables['time'] = timefield_integer_to_time($variables['settings']['display_format'], $variables['element']['value']);
  820. if (isset($variables['element']['value2'])) {
  821. $variables['time'] .= '-' . timefield_integer_to_time($variables['settings']['display_format'], $variables['element']['value2']);
  822. }
  823. $variables['classes'] = implode(' ', $variables['classes_array']);
  824. }
  825. /**
  826. * Helper function to build time format settings appropriate for use with PHP
  827. * date function.
  828. */
  829. function timefield_build_time_format($settings) {
  830. $format = $settings['hour'];
  831. $format .= $settings['minute'] == 'none' ? '' : $settings['separator'] . $settings['minute'];
  832. $format .= $settings['period'] == 'none' ? '' : $settings['period_separator'] . $settings['period'];
  833. return $format;
  834. }
  835. /**
  836. * Helper function to return time value from a timefield integer.
  837. *
  838. * @param array $settings
  839. * Field formatter settings. This is a structured array used to format a date
  840. * with PHP's date() function. This array has the following keys:
  841. * -separator
  842. * The character(s) the go(es) between the hour and minute value
  843. * -period_separator
  844. * The character(s) the go(es) between the time string and the period
  845. * (AM/PM)
  846. * -period
  847. * The PHP formatting option for period, or "none" to omit display
  848. * -hour
  849. * The PHP formatting option for hour
  850. * -minute
  851. * The PHP formatting option for minute
  852. * @see _timefield_time_part_format() for some possible options. It is worth
  853. * noting that no assumptions are made about how one wishes to format date
  854. * output, e.g., 24-hour formatted times are not assumed to not have AM/PM
  855. * period information.
  856. * @param integer $value
  857. * Integer offset from midnight to be converted to human-readable time. This
  858. * value is basically number of seconds from midnight. If you wish to
  859. * to show a time +1 day, your value can be greater than 86400.
  860. *
  861. * @return string
  862. * Human-readable time string
  863. */
  864. function timefield_integer_to_time($settings, $value) {
  865. $format = timefield_build_time_format($settings);
  866. if (isset($value)) {
  867. if ($value >= 86400) {
  868. $value = $value - 86400;
  869. }
  870. return date($format, mktime(0, 0, $value));
  871. }
  872. else {
  873. return '';
  874. }
  875. }
  876. /**
  877. * Helper function to return duration value from a timefield integer value, in
  878. * specified format.
  879. *
  880. * @param integer $value
  881. * Time value in seconds
  882. * @param string $format
  883. * Out format options. Possible options are:
  884. * -hours
  885. * -minutes
  886. * -seconds
  887. * -time
  888. *
  889. * @return string
  890. * Integer cast to string or string depending on $format passed.
  891. */
  892. function timefield_integer_to_duration($value, $format) {
  893. switch ($format) {
  894. case 'hours':
  895. return (string) round(($value / 60 / 60), 2);
  896. break;
  897. case 'minutes':
  898. return (string) round(($value / 60), 2);
  899. break;
  900. case 'seconds':
  901. return (string) $value;
  902. break;
  903. case 'time':
  904. return date('g:i', mktime(NULL, NULL, $value));
  905. break;
  906. }
  907. }
  908. /**
  909. * Helper function to return integer value offset from midnight from time
  910. * format.
  911. *
  912. * @param string $value
  913. * Time format that should be parsable via date_parse().
  914. */
  915. function timefield_time_to_integer($value) {
  916. $time = date_parse($value);
  917. $output = 0;
  918. if ($time['error_count'] == 0) {
  919. $output += $time['hour'] * 60 * 60;
  920. $output += $time['minute'] * 60;
  921. $output += $time['second'];
  922. return $output;
  923. }
  924. else {
  925. return 0;
  926. }
  927. }
  928. /**
  929. * Helper function to return duration value from 2 values, in specified format.
  930. *
  931. * @param integer $value
  932. * First time value
  933. * @param integer $value2
  934. * Second time value
  935. * @param string $format
  936. * Out format options. Possible options are:
  937. * -hours
  938. * -minutes
  939. * -seconds
  940. * -time
  941. *
  942. * @return mixed
  943. * Integer or string depending on $format passed
  944. */
  945. function timefield_time_to_duration($value, $value2, $format) {
  946. if ($value2 < $value) {
  947. $value2 += 86400;
  948. }
  949. $duration = $value2 - $value;
  950. switch ($format) {
  951. case 'hours':
  952. return round(($duration / 60 / 60), 2);
  953. break;
  954. case 'minutes':
  955. return round(($duration / 60), 2);
  956. break;
  957. case 'seconds':
  958. return $duration;
  959. break;
  960. case 'time':
  961. return date('g:i', mktime(NULL, NULL, $duration));
  962. break;
  963. }
  964. return 0;
  965. }
  966. /**
  967. * Helper function to build an options array
  968. */
  969. function _timefield_time_part_format($part) {
  970. $values = array(
  971. 'hour' => array(
  972. 'g' => t('12-hour format of an hour without leading zeros'),
  973. 'G' => t('24-hour format of an hour without leading zeros'),
  974. 'h' => t('12-hour format of an hour with leading zeros'),
  975. 'H' => t('24-hour format of an hour with leading zeros'),
  976. ),
  977. 'minute' => array(
  978. 'i' => t('Minutes with leading Zeros'),
  979. 'none' => t('Do not display minutes'),
  980. ),
  981. 'period' => array(
  982. 'a' => t('Lowercase Ante meridiem and Post meridiem (am/pm)'),
  983. 'A' => t('Uppercase Ante meridiem and Post meridiem (AM/PM)'),
  984. 'none' => t('Do not display period'),
  985. ),
  986. );
  987. return $values[$part];
  988. }
  989. /**
  990. * Helper function to build options array
  991. */
  992. function _timefield_duration_options($current_option = NULL) {
  993. $values = array(
  994. 'hours' => t('Duration in decimal hours, e.g. 1.5'),
  995. 'seconds' => t('Duration in seconds'),
  996. 'minutes' => t('Duration in minutes'),
  997. 'time' => t('Duration in time format hours, e.g. 1:30'),
  998. );
  999. if (is_null($current_option)) {
  1000. return $values;
  1001. }
  1002. else {
  1003. return $values[$current_option];
  1004. }
  1005. }
  1006. /**
  1007. * Helper function to return settings usable for the jquery.timepicker.js
  1008. * library.
  1009. *
  1010. */
  1011. function _timefield_js_settings($class, $settings) {
  1012. $js_settings = array(
  1013. $class => array(
  1014. 'showLeadingZero' => $settings['showLeadingZero'],
  1015. 'timeSeparator' => $settings['separator'],
  1016. 'showPeriod' => $settings['showPeriod'],
  1017. 'showPeriodLabels' => $settings['showPeriod'],
  1018. 'periodSeparator' => $settings['periodSeparator'],
  1019. 'amPmText' => array($settings['am_text'], $settings['pm_text']),
  1020. 'showMinutesLeadingZero' => TRUE,
  1021. 'showCloseButton' => $settings['showCloseButton'],
  1022. 'closeButtonText' => $settings['closeButtonText'],
  1023. 'showNowButton' => $settings['showNowButton'],
  1024. 'nowButtonText' => $settings['nowButtonText'],
  1025. 'showDeselectButton' => $settings['showDeselectButton'],
  1026. 'deselectButtonText' => $settings['deselectButtonText'],
  1027. 'myPosition' => $settings['myPosition'],
  1028. 'atPosition' => $settings['atPosition'],
  1029. ),
  1030. );
  1031. return $js_settings;
  1032. }
  1033. /**
  1034. * Helper function to map format settings for jQuery timepicker widget to PHP
  1035. * format settings, so that input formats are consistent.
  1036. *
  1037. */
  1038. function _timefield_map_input_format_to_display_format($date, $display_format) {
  1039. $date_value = date_parse($date);
  1040. if (count($date_value['errors'])) {
  1041. return '';
  1042. }
  1043. if ($display_format['showPeriod']) {
  1044. switch ($date_value['hour']) {
  1045. case 0:
  1046. $format = 12;
  1047. break;
  1048. case ($date_value['hour'] > 12):
  1049. $format = $date_value['hour'] - 12;
  1050. break;
  1051. default:
  1052. $format = $date_value['hour'];
  1053. break;
  1054. }
  1055. }
  1056. else {
  1057. $format = $date_value['hour'];
  1058. }
  1059. $format .= $display_format['separator'];
  1060. $format .= str_pad((string) $date_value['minute'], 2, "0", STR_PAD_LEFT);
  1061. if ($display_format['showPeriod']) {
  1062. $format .= $display_format['periodSeparator'];
  1063. $format .= $date_value['hour'] >= 12 ? $display_format['pm_text'] : $display_format['am_text'];
  1064. }
  1065. return $format;
  1066. }
  1067. /**
  1068. * Implements hook_views_api().
  1069. */
  1070. function timefield_views_api() {
  1071. return array(
  1072. 'api' => 3,
  1073. 'path' => drupal_get_path('module', 'timefield') . '/views',
  1074. );
  1075. }
  1076. /**
  1077. * Helper function days array
  1078. */
  1079. function _timefield_weekly_summary_days() {
  1080. $days = array(
  1081. 'mon' => t('Monday'),
  1082. 'tue' => t('Tuesday'),
  1083. 'wed' => t('Wednesday'),
  1084. 'thu' => t('Thursday'),
  1085. 'fri' => t('Friday'),
  1086. 'sat' => t('Saturday'),
  1087. 'sun' => t('Sunday'),
  1088. );
  1089. return $days;
  1090. }
  1091. /**
  1092. * Map first day of week
  1093. */
  1094. function _timefield_weekly_summary_days_map($item) {
  1095. $days = _timefield_weekly_summary_days();
  1096. $output = array();
  1097. foreach ($days as $day => $label) {
  1098. $output[$day] = ($item[$day] === 0 || $item[$day] == "0") ? 0 : $day;
  1099. }
  1100. return $output;
  1101. }
  1102. /**
  1103. * Callback to create the property info for timefield.
  1104. *
  1105. * @see timefield_field_info()
  1106. */
  1107. function timefield_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
  1108. $name = $field['field_name'];
  1109. $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name];
  1110. $property['type'] = ($field['cardinality'] != 1) ? 'list<timefield>' : 'timefield';
  1111. $property['getter callback'] = 'entity_metadata_field_verbatim_get';
  1112. $property['setter callback'] = 'entity_metadata_field_verbatim_set';
  1113. $property['property info'] = array(
  1114. 'value' => array(
  1115. 'label' => t('Integer offset for the Start Time'),
  1116. 'type' => 'integer',
  1117. 'getter callback' => 'entity_property_verbatim_get',
  1118. ),
  1119. 'value_formatted' => array(
  1120. 'label' => t('Start Time'),
  1121. 'type' => 'text',
  1122. 'getter callback' => 'entity_property_verbatim_get',
  1123. ),
  1124. );
  1125. if (!empty($field['settings']['totime'])) {
  1126. $property['property info']['value2'] = array(
  1127. 'label' => t('Integer Offset for the End Time'),
  1128. 'type' => 'integer',
  1129. 'getter callback' => 'entity_property_verbatim_get',
  1130. );
  1131. $property['property info']['value2_formatted'] = array(
  1132. 'label' => t('End Time'),
  1133. 'type' => 'text',
  1134. 'getter callback' => 'entity_property_verbatim_get',
  1135. );
  1136. }
  1137. if ($field['settings']['weekly_summary']) {
  1138. $property['property info']['label'] = array(
  1139. 'label' => t('Label'),
  1140. 'description' => t('The label of this weekly schedule'),
  1141. 'type' => 'text',
  1142. 'getter callback' => 'entity_property_verbatim_get',
  1143. 'setter callback' => 'entity_property_verbatim_set',
  1144. );
  1145. }
  1146. if ($field['settings']['weekly_summary'] || $field['settings']['weekly_summary_with_label']) {
  1147. foreach (_timefield_weekly_summary_days() as $index => $label) {
  1148. $property['property info'][$index] = array(
  1149. 'label' => $label,
  1150. 'description' => t('If this schedule applies to !day', array('!day' => $label)),
  1151. 'type' => 'boolean',
  1152. 'getter callback' => 'entity_property_verbatim_get',
  1153. 'setter callback' => 'entity_property_verbatim_set',
  1154. );
  1155. }
  1156. }
  1157. unset($property['query callback']);
  1158. }
  1159. /**
  1160. * Build a header for a schedule table
  1161. */
  1162. function _timefield_weekly_summary_build_header($first_day) {
  1163. $days = _timefield_weekly_summary_days();
  1164. $day_list = $days;
  1165. $header = array('time' => t('Time'));
  1166. $found_header = FALSE;
  1167. foreach ($days as $index => $day) {
  1168. if ($index == $first_day) {
  1169. $found_header = TRUE;
  1170. }
  1171. if ($found_header) {
  1172. $header[$index] = $day;
  1173. unset($day_list[$index]);
  1174. }
  1175. }
  1176. $header += $day_list;
  1177. return $header;
  1178. }
  1179. /**
  1180. * Add rows to the table
  1181. */
  1182. function timefield_weekly_summary_build_rows($item, $header, $settings) {
  1183. _timefield_weekly_summary_explode_items($item);
  1184. $times = _timefield_weekly_summary_build_time_column($settings);
  1185. $abs_start = timefield_time_to_integer($settings['absolute_start']);
  1186. $abs_end = timefield_time_to_integer($settings['absolute_end']);
  1187. $total_range = $abs_end - $abs_start;
  1188. $cell_data = array();
  1189. $count = 0;
  1190. foreach ($times as $time_index => $time) {
  1191. $row['time'] = array('data' => $time['display']);
  1192. if (!isset($cell_data[$time_index])) {
  1193. $cell_data[$time_index] = array();
  1194. }
  1195. foreach ($header as $index => $label) {
  1196. if ($index == 'time') {
  1197. continue;
  1198. }
  1199. if (!isset($cell_data[$time_index][$index])) {
  1200. $cell_data[$time_index][$index] = array();
  1201. }
  1202. foreach ($item as $i => $v) {
  1203. if ($v['value'] >= $time['start'] && $v['value'] < $time['stop'] && $v[$index] == '1') {
  1204. $row_data = array(
  1205. 'element' => $v,
  1206. 'settings' => $settings,
  1207. 'day' => array($index => $label),
  1208. );
  1209. $row_data['span_time'] = ($v['value2'] - $v['value']) / ($total_range) * 100;
  1210. $row_data['offset_time'] = ($v['value'] - $abs_start) / ($total_range) * 100;
  1211. $cell_data[$time_index][$index][] = theme('timefield_weekly_summary_minical_box', $row_data);
  1212. }
  1213. }
  1214. $row[$index] = array('data' => !empty($cell_data[$time_index][$index]) ? implode(' ', $cell_data[$time_index][$index]) : '');
  1215. }
  1216. $rows[] = array('data' => $row, 'class' => array('row-' . $count));
  1217. $count += 1;
  1218. }
  1219. return $rows;
  1220. }
  1221. /**
  1222. * Helper function to explode all items in a timefield value array so that
  1223. * we can sort across multivalue fields
  1224. */
  1225. function _timefield_weekly_summary_explode_items(&$items) {
  1226. $new_array = array();
  1227. foreach ($items as $item) {
  1228. foreach (array_keys(_timefield_weekly_summary_days()) as $day) {
  1229. if ($item[$day]) {
  1230. $ar = array(
  1231. 'label' => isset($item['label']) ? $item['label'] : '',
  1232. 'mon' => FALSE,
  1233. 'tue' => FALSE,
  1234. 'wed' => FALSE,
  1235. 'thu' => FALSE,
  1236. 'fri' => FALSE,
  1237. 'sat' => FALSE,
  1238. 'sun' => FALSE,
  1239. 'value' => $item['value'],
  1240. 'value2' => $item['value2'],
  1241. );
  1242. $ar[$day] = TRUE;
  1243. $new_array[] = $ar;
  1244. }
  1245. }
  1246. }
  1247. // Sort our new array.
  1248. uasort($new_array, '_timefield_weekly_summary_time_sort');
  1249. $items = $new_array;
  1250. }
  1251. /**
  1252. * Uasort callback function
  1253. */
  1254. function _timefield_weekly_summary_time_sort($a, $b) {
  1255. $calca = _timefield_weekly_summary_week_time_offset($a);
  1256. $calcb = _timefield_weekly_summary_week_time_offset($b);
  1257. if ($calca == $calcb) {
  1258. return 0;
  1259. }
  1260. return ($calca < $calcb) ? -1 : 1;
  1261. }
  1262. /**
  1263. * Calculate offset from beginning of week.
  1264. */
  1265. function _timefield_weekly_summary_week_time_offset($elem) {
  1266. $multiplier = 60 * 60 * 24;
  1267. if ($elem['mon']) {
  1268. $mult = 0;
  1269. }
  1270. if ($elem['tue']) {
  1271. $mult = 1;
  1272. }
  1273. if ($elem['wed']) {
  1274. $mult = 2;
  1275. }
  1276. if ($elem['thu']) {
  1277. $mult = 3;
  1278. }
  1279. if ($elem['fri']) {
  1280. $mult = 4;
  1281. }
  1282. if ($elem['sat']) {
  1283. $mult = 5;
  1284. }
  1285. if ($elem['sun']) {
  1286. $mult = 6;
  1287. }
  1288. return $multiplier * $mult + $elem['value'];
  1289. }
  1290. /**
  1291. * Helper function to build the time column for the minical table.
  1292. */
  1293. function _timefield_weekly_summary_build_time_column($settings, $start_times = array()) {
  1294. $time_array = array();
  1295. $start = timefield_time_to_integer($settings['absolute_start']);
  1296. $end = timefield_time_to_integer($settings['absolute_end']);
  1297. $total_range = $end - $start;
  1298. $step_amount = $settings['range'] * 60;
  1299. $steps = ceil($total_range / $step_amount);
  1300. for ($index = 0; $index < $steps; $index++) {
  1301. if ($index == 0) {
  1302. $current = $start;
  1303. }
  1304. $time_array[$current] = array(
  1305. 'start' => $current,
  1306. 'stop' => $current + $step_amount,
  1307. 'display' => timefield_integer_to_time($settings['column_format'], $current) . ' - ' . timefield_integer_to_time($settings['column_format'], $current + $step_amount),
  1308. 'last_row' => ($index + 1 == $steps) ? TRUE : FALSE,
  1309. );
  1310. $current += $step_amount;
  1311. }
  1312. return $time_array;
  1313. }