addressfield_tokens.components.inc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. <?php
  2. /**
  3. * @file
  4. * Webform Component information for an address field type.
  5. */
  6. /**
  7. * Specify the default properties of a component.
  8. *
  9. * @return array
  10. * An array defining the default structure of a component.
  11. */
  12. function _webform_defaults_addressfield() {
  13. return array(
  14. 'name' => '',
  15. 'form_key' => NULL,
  16. 'required' => 0,
  17. 'pid' => 0,
  18. 'weight' => 0,
  19. 'extra' => array(
  20. 'title_display' => 0,
  21. 'private' => FALSE,
  22. 'attributes' => array(),
  23. 'description' => '',
  24. 'available_countries' => array(),
  25. 'default_country' => '',
  26. 'format_handlers' => array(),
  27. 'csv_separate' => 0,
  28. ),
  29. );
  30. }
  31. /**
  32. * Generate the form for editing a component.
  33. *
  34. * Create a set of form elements to be displayed on the form for editing this
  35. * component. Use care naming the form items, as this correlates directly to the
  36. * database schema. The component "Name" and "Description" fields are added to
  37. * every component type and are not necessary to specify here (although they
  38. * may be overridden if desired).
  39. *
  40. * @param mixed $component
  41. * A Webform component array.
  42. *
  43. * @return array
  44. * An array of form items to be displayed on the edit component page
  45. */
  46. function _webform_edit_addressfield($component) {
  47. $form = array();
  48. $form['extra']['available_countries'] = array(
  49. '#type' => 'select',
  50. '#multiple' => TRUE,
  51. '#title' => t('Available countries'),
  52. '#description' => t('If no countries are selected, all countries will be available.'),
  53. '#options' => _addressfield_country_options_list(),
  54. '#default_value' => $component['extra']['available_countries'],
  55. );
  56. $form['extra']['default_country'] = array(
  57. '#type' => 'select',
  58. '#multiple' => FALSE,
  59. '#title' => t('Default country'),
  60. '#description' => t('Select which country should be selected as the default.'),
  61. '#options' => array_merge(array(0 => t('- None -')), _addressfield_country_options_list()),
  62. '#default_value' => $component['extra']['default_country'],
  63. );
  64. $form['extra']['format_handlers'] = array(
  65. '#type' => 'checkboxes',
  66. '#title' => t('Format handlers'),
  67. '#options' => addressfield_format_plugins_options(),
  68. '#required' => TRUE,
  69. '#default_value' => !empty($component['extra']['format_handlers']) ? $component['extra']['format_handlers'] : array('address'),
  70. );
  71. $form['extra']['csv_separate'] = array(
  72. '#type' => 'radios',
  73. '#title' => t('CSV download'),
  74. '#description' => t('How would you like addresses presented in CSV downloads?'),
  75. '#options' => array(
  76. 0 => t('Display entire address in a single column'),
  77. 1 => t('Display each address component in a separate column'),
  78. ),
  79. '#default_value' => $component['extra']['csv_separate'],
  80. );
  81. return $form;
  82. }
  83. /**
  84. * Render a Webform component to be part of a form.
  85. *
  86. * @param mixed $component
  87. * A Webform component array.
  88. * @param mixed $value
  89. * If editing an existing submission or resuming a draft, this will contain
  90. * an array of values to be shown instead of the default in the component
  91. * configuration. This value will always be an array, keyed numerically for
  92. * each value saved in this field.
  93. * @param bool $filter
  94. * Whether or not to filter the contents of descriptions and values when
  95. * rendering the component. Values need to be unfiltered to be editable by
  96. * Form Builder.
  97. *
  98. * @return array
  99. * Form element.
  100. *
  101. * @see _webform_client_form_add_component()
  102. */
  103. function _webform_render_addressfield($component, $value = NULL, $filter = TRUE) {
  104. $element = array(
  105. '#type' => 'fieldset',
  106. '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
  107. '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
  108. '#attributes' => $component['extra']['attributes'],
  109. '#theme_wrappers' => array('webform_element'),
  110. '#description' => $filter ? _webform_filter_descriptions($component['extra']['description']) : $component['extra']['description'],
  111. '#required' => $component['required'],
  112. '#weight' => $component['weight'],
  113. '#translatable' => array(
  114. 'title',
  115. 'description',
  116. ),
  117. );
  118. $available = !empty($component['extra']['available_countries']) ? $component['extra']['available_countries'] : NULL;
  119. // Get the current address.
  120. $address = _addressfield_tokens_expand_value($value);
  121. if (empty($address)) {
  122. if (!empty($component['value'])) {
  123. $address = $component['value'];
  124. }
  125. else {
  126. $address = _webform_addressfield($component['cid']);
  127. }
  128. }
  129. if (empty($address)) {
  130. $address = _webform_addressfield_default_values($available, $component);
  131. }
  132. // Generate the address form.
  133. $context = array(
  134. 'mode' => 'form',
  135. 'instance' => array(
  136. 'required' => $component['required'],
  137. ),
  138. 'form_key' => $component['form_key'],
  139. );
  140. $handlers = !empty($component['extra']['format_handlers']) ? $component['extra']['format_handlers'] : array('address');
  141. $element += addressfield_generate($address, $handlers, $context);
  142. if (empty($address['country'])) {
  143. $element['street_block']['#access'] = FALSE;
  144. $element['locality_block']['#access'] = FALSE;
  145. }
  146. if (isset($element['country'])) {
  147. if (!empty($available)) {
  148. $element['country']['#options'] = array_intersect_key($element['country']['#options'], $available);
  149. // Hide the country element only if there is one option and the whole field
  150. // is required, otherwise there will always be an additional None option.
  151. // @see addressfield_format_address_hide_country()
  152. if (!empty($handlers['address-hide-country']) && count($element['country']['#options']) == 1 && $component['required']) {
  153. $element['country']['#access'] = FALSE;
  154. }
  155. }
  156. $element['country']['#default_value'] = $address['country'];
  157. $element['country']['#element_validate'] = array('_webform_addressfield_country_validate');
  158. $element['country']['#cid'] = $component['cid'];
  159. $element['country']['#limit_validation_errors'] = array();
  160. }
  161. $form_state = array();
  162. drupal_alter('field_widget_addressfield_standard_form', $element, $form_state, $context);
  163. return $element;
  164. }
  165. function _webform_addressfield_default_values($available, $component) {
  166. $default_country = !empty($component['extra']['default_country'])
  167. ? $component['extra']['default_country']
  168. : addressfield_tokens_default_country();
  169. $default_values = array(
  170. 'country' => $default_country,
  171. 'name_line' => '',
  172. 'first_name' => '',
  173. 'last_name' => '',
  174. 'organisation_name' => '',
  175. 'administrative_area' => '',
  176. 'sub_administrative_area' => '',
  177. 'locality' => '',
  178. 'dependent_locality' => '',
  179. 'postal_code' => '',
  180. 'thoroughfare' => '',
  181. 'premise' => '',
  182. 'sub_premise' => '',
  183. 'data' => '',
  184. );
  185. return ($default_values);
  186. }
  187. /**
  188. * Stores an addressfield submitted in a webform component.
  189. *
  190. * Ideally store it in the $form_state instead, but there appears to be no way
  191. * to get it to actually pass through to _webform_render_addressfield().
  192. *
  193. * @param int $cid
  194. * The ID of the webform component.
  195. * @param mixed $address
  196. * If set, this address will be stored with the given $cid.
  197. *
  198. * @return array
  199. * The address stored with the given $cid, if there is one; otherwise, NULL.
  200. */
  201. function _webform_addressfield($cid, $address = NULL) {
  202. $out = &drupal_static(__FUNCTION__, array());
  203. if (isset($address)) {
  204. $out[$cid] = $address;
  205. }
  206. if (isset($out[$cid])) {
  207. return $out[$cid];
  208. }
  209. return NULL;
  210. }
  211. /**
  212. * Validates a country, and if changed, rebuilds the form for the new country.
  213. */
  214. function _webform_addressfield_country_validate(&$element, &$form_state) {
  215. // If the country was changed, rebuild the form.
  216. if (!isset($element['#default_value']) || $element['#default_value'] != $element['#value']) {
  217. $form_state['rebuild'] = TRUE;
  218. }
  219. $cid = $element['#cid'];
  220. $parents = $element['#parents'];
  221. array_pop($parents);
  222. // Search through the form values to find the current address.
  223. $address = drupal_array_get_nested_value($form_state['values'], $parents);
  224. _webform_addressfield($cid, $address);
  225. }
  226. /**
  227. * Display the result of a submission for a component.
  228. *
  229. * The output of this function will be displayed under the "Results" tab then
  230. * "Submissions". This should output the saved data in some reasonable manner.
  231. *
  232. * @param mixed $component
  233. * A Webform component array.
  234. * @param mixed $value
  235. * An array of information containing the submission result, directly
  236. * correlating to the webform_submitted_data database table schema.
  237. * @param string $format
  238. * Either 'html' or 'text'. Defines the format that the content should be
  239. * returned as. Make sure that returned content is run through check_plain()
  240. * or other filtering functions when returning HTML.
  241. *
  242. * @return array
  243. * A renderable element containing at the very least these properties:
  244. * - #title
  245. * - #weight
  246. * - #component
  247. * - #format
  248. * - #value
  249. * Webform also uses #theme_wrappers to output the end result to the user,
  250. * which will properly format the label and content for use within an e-mail
  251. * (such as wrapping the text) or as HTML (ensuring consistent output).
  252. */
  253. function _webform_display_addressfield($component, $value, $format = 'html') {
  254. $address = _addressfield_tokens_expand_value($value);
  255. return array(
  256. '#title' => $component['name'],
  257. '#weight' => $component['weight'],
  258. '#theme' => $format == 'html' ? 'addressfield_formatter' : 'addressfield_formatter__linear',
  259. '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
  260. '#post_render' => array('webform_element_wrapper'),
  261. '#component' => $component,
  262. '#format' => $format,
  263. '#address' => $address ? $address : NULL,
  264. '#handlers' => $component['extra']['format_handlers'],
  265. );
  266. }
  267. /**
  268. * A hook for changing the input values before saving to the database.
  269. *
  270. * Webform expects a component to consist of a single field, or a single array
  271. * of fields. If you have a component that requires a deeper form tree
  272. * you must flatten the data into a single array using this callback
  273. * or by setting #parents on each field to avoid data loss and/or unexpected
  274. * behavior.
  275. * Note that Webform will save the result of this function directly into the
  276. * database.
  277. *
  278. * @param mixed $component
  279. * A Webform component array.
  280. * @param mixed $value
  281. * The POST data associated with the user input.
  282. *
  283. * @return array
  284. * An array of values to be saved into the database. Note that this should be
  285. * a numerically keyed array.
  286. */
  287. function _webform_submit_addressfield($component, $value) {
  288. return serialize($value);
  289. }
  290. /**
  291. * Calculate and returns statistics about results for this component.
  292. *
  293. * This takes into account all submissions to this webform. The output of this
  294. * function will be displayed under the "Results" tab then "Analysis".
  295. *
  296. * @param mixed $component
  297. * An array of information describing the component, directly correlating to
  298. * the webform_component database schema.
  299. * @param mixed $sids
  300. * An optional array of submission IDs (sid). If supplied, the analysis will
  301. * be limited to these sids.
  302. * @param bool $single
  303. * Boolean flag determining if the details about a single component are being
  304. * shown. May be used to provided detailed information about a single
  305. * component's analysis, such as showing "Other" options within a select list.
  306. *
  307. * @return array
  308. * An array of data rows, each containing a statistic for this component's
  309. * submissions.
  310. */
  311. function _webform_analysis_addressfield($component, $sids = array(), $single = FALSE) {
  312. // @todo Update this function
  313. // Generate the list of options and questions.
  314. $query = db_select('webform_submitted_data', 'wsd')
  315. ->fields('wsd', array('data'))
  316. ->condition('nid', $component['nid'])
  317. ->condition('cid', $component['cid']);
  318. if (count($sids)) {
  319. $query->condition('sid', $sids, 'IN');
  320. }
  321. $non_blanks = 0;
  322. $submissions = 0;
  323. $results = $query->execute();
  324. foreach ($results as $row) {
  325. if (drupal_strlen(trim($row->data)) > 0) {
  326. $non_blanks++;
  327. }
  328. $submissions++;
  329. }
  330. $rows[0] = array(
  331. t('Left Blank'),
  332. ($submissions - $non_blanks),
  333. );
  334. $rows[1] = array(
  335. t('User entered value'),
  336. $non_blanks,
  337. );
  338. return array('table_rows' => $rows);
  339. }
  340. /**
  341. * Return the result of a component value for display in a table.
  342. *
  343. * The output of this function will be displayed under the "Results" tab then
  344. * "Table".
  345. *
  346. * @param mixed $component
  347. * A Webform component array.
  348. * @param mixed $value
  349. * An array of information containing the submission result, directly
  350. * correlating to the webform_submitted_data database schema.
  351. *
  352. * @return string
  353. * Textual output formatted for human reading.
  354. */
  355. function _webform_table_addressfield($component, $value) {
  356. $address = _addressfield_tokens_expand_value($value);
  357. if ($address) {
  358. return theme('addressfield_formatter', array('address' => $address));
  359. }
  360. return '';
  361. }
  362. /**
  363. * Return the header for this component to be displayed in a CSV file.
  364. *
  365. * The output of this function will be displayed under the "Results" tab then
  366. * "Download".
  367. *
  368. * @param mixed $component
  369. * A Webform component array.
  370. * @param mixed $export_options
  371. * An array of options that may configure export of this field.
  372. *
  373. * @return array
  374. * An array of data to be displayed in the first three rows of a CSV file, not
  375. * including either prefixed or trailing commas.
  376. */
  377. function _webform_csv_headers_addressfield($component, $export_options) {
  378. $header = array();
  379. if (!empty($component['extra']['csv_separate']) && $component['extra']['csv_separate'] == 1) {
  380. $header[0] = array();
  381. $header[1] = array();
  382. $header[2] = array();
  383. foreach (addressfield_tokens_property_names() as $key => $name) {
  384. $header[0][] = '';
  385. $header[1][] = (empty($header[1])) ? $component['name'] : '';
  386. $header[2][] = $name;
  387. }
  388. }
  389. else {
  390. $header[0] = array('');
  391. $header[1] = array('');
  392. $header[2] = array($component['name']);
  393. }
  394. return $header;
  395. }
  396. /**
  397. * Format the submitted data of a component for CSV downloading.
  398. *
  399. * The output of this function will be displayed under the "Results" tab then
  400. * "Download".
  401. *
  402. * @param mixed $component
  403. * A Webform component array.
  404. * @param mixed $export_options
  405. * An array of options that may configure export of this field.
  406. * @param mixed $value
  407. * An array of information containing the submission result, directly
  408. * correlating to the webform_submitted_data database schema.
  409. *
  410. * @return array
  411. * An array of items to be added to the CSV file. Each value within the array
  412. * will be another column within the file. This function is called once for
  413. * every row of data.
  414. */
  415. function _webform_csv_data_addressfield($component, $export_options, $value) {
  416. $address = _addressfield_tokens_expand_value($value);
  417. if (!empty($component['extra']['csv_separate']) && $component['extra']['csv_separate'] == 1) {
  418. // Each address component should be in a separate column.
  419. $return = array();
  420. foreach (addressfield_tokens_property_names() as $key => $name) {
  421. $return[] = (isset($address[$key])) ? $address[$key] : '';
  422. }
  423. return $return;
  424. }
  425. else {
  426. // The entire address should be displayed in the one column.
  427. if ($address) {
  428. return theme('addressfield_formatter__linear', array('address' => $address));
  429. }
  430. return '';
  431. }
  432. }
  433. /**
  434. * Expand a raw address submission as loaded from the database to an array.
  435. *
  436. * @param string $value
  437. * An array of information containing the submission result, directly
  438. * correlating to the {webform_submitted_data} database schema.
  439. *
  440. * @return array|false
  441. * An associative array of address fields, or FALSE on failure.
  442. */
  443. function _addressfield_tokens_expand_value($value) {
  444. if (isset($value[0]) && is_string($value[0])) {
  445. return unserialize($value[0]);
  446. }
  447. return FALSE;
  448. }