name.module 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917
  1. <?php
  2. /**
  3. * @file
  4. * Defines an API for displaying and inputing names.
  5. *
  6. * @todo Make sure that all labels are based on the _name_translations()
  7. * function and use a name:: prefix. This can be parsed out here to allow
  8. * string overrides to work and to integrate with i18n too.
  9. * t('!name_given', array('!name_given' => t('Given')))
  10. */
  11. // To be removed after fixing Feeds issue 1224836, 1139676.
  12. include_once 'name.feeds.inc';
  13. /**
  14. * This is the main function that formats a name from an array of components.
  15. *
  16. * @param array $name_components
  17. * A keyed array of name components.
  18. * These are: title, given, middle, family, generational and credentials.
  19. * @param string $format
  20. * The string specifying what format to use.
  21. * @param array $settings
  22. * A keyed array of additional parameters to pass into the function.
  23. * Includes:
  24. * 'object' - An object or array.
  25. * This entity is used for Token module subsitutions.
  26. * Currently not used.
  27. * 'type' - A string.
  28. * The entity identifier: node, user, etc.
  29. */
  30. function name_format($name_components, $format, $settings = array()) {
  31. module_load_include('inc', 'name', 'includes/name.parser');
  32. return _name_format($name_components, $format, $settings);
  33. }
  34. /**
  35. * Handles the initialization of the Name module settings that are stored in
  36. * the {variables} table.
  37. */
  38. function name_settings($key = NULL) {
  39. $settings = variable_get('name_settings', array());
  40. $settings += array(
  41. 'default_format' => '((((t+ig)+im)+if)+is)+jc',
  42. 'sep1' => ' ',
  43. 'sep2' => ', ',
  44. 'sep3' => '',
  45. );
  46. if ($key) {
  47. return $settings[$key];
  48. }
  49. return $settings;
  50. }
  51. /**
  52. * Helper function to find the components used.
  53. */
  54. function name_get_instance_components($field_components, $instance_components) {
  55. return array_filter($instance_components) ? $instance_components : $field_components;
  56. }
  57. /**
  58. * Implements hook_menu().
  59. */
  60. function name_menu() {
  61. $items = array();
  62. // Provides autocomplete functionality for name widgets.
  63. $items['name/autocomplete/%/%'] = array(
  64. 'title' => 'Name autocomplete',
  65. 'page callback' => 'name_autocomplete',
  66. 'page arguments' => array(2, 3),
  67. 'access arguments' => array('access content'),
  68. 'type' => MENU_CALLBACK,
  69. 'file' => 'name.admin.inc',
  70. );
  71. // Admin menu items.
  72. $items['admin/config/content/name'] = array(
  73. 'title' => 'Name field',
  74. 'page callback' => 'name_list_custom_formats',
  75. 'description' => 'Configure, edit and maintain names and name custom formats.',
  76. 'access arguments' => array('administer site configuration'),
  77. 'file' => 'name.admin.inc',
  78. 'type' => MENU_NORMAL_ITEM,
  79. );
  80. $items['admin/config/content/name/formats'] = array(
  81. 'title' => 'Name formats',
  82. 'type' => MENU_DEFAULT_LOCAL_TASK,
  83. 'weight' => -10,
  84. );
  85. $items['admin/config/content/name/add'] = array(
  86. 'title' => 'Add name format',
  87. 'page callback' => 'drupal_get_form',
  88. 'page arguments' => array('name_custom_formats_form'),
  89. 'access arguments' => array('administer site configuration'),
  90. 'file' => 'name.admin.inc',
  91. 'type' => MENU_LOCAL_ACTION,
  92. );
  93. $items['admin/config/content/name/settings'] = array(
  94. 'title' => 'Settings',
  95. 'page callback' => 'drupal_get_form',
  96. 'page arguments' => array('name_admin_settings_form'),
  97. 'access arguments' => array('administer site configuration'),
  98. 'file' => 'name.admin.inc',
  99. 'type' => MENU_LOCAL_TASK,
  100. 'weight' => 10,
  101. );
  102. $items['admin/config/content/name/%'] = array(
  103. 'title' => 'Edit custom format',
  104. 'page callback' => 'name_custom_format_edit',
  105. 'page arguments' => array(4),
  106. 'access arguments' => array('administer site configuration'),
  107. 'file' => 'name.admin.inc',
  108. 'type' => MENU_CALLBACK,
  109. );
  110. $items['admin/config/content/name/%/delete'] = array(
  111. 'title' => 'Delete custom name format',
  112. 'page callback' => 'drupal_get_form',
  113. 'page arguments' => array('name_custom_format_delete_form', 4),
  114. 'access arguments' => array('administer site configuration'),
  115. 'file' => 'name.admin.inc',
  116. 'type' => MENU_CALLBACK,
  117. );
  118. return $items;
  119. }
  120. /**
  121. * Implements hook_theme().
  122. */
  123. function name_theme() {
  124. $theme = array(
  125. // Themes an individual name element. This is only used by the webform
  126. // integration but will be extended at some stage to theme the formatter
  127. // items too.
  128. 'name_item' => array(
  129. 'variables' => array('item' => array(), 'format' => NULL, 'settings' => array()),
  130. ),
  131. // This themes an element into the "name et al" format.
  132. 'name_item_list' => array(
  133. 'variables' => array('items' => array(), 'settings' => array()),
  134. ),
  135. // Themes the FAPI element.
  136. 'name_element' => array(
  137. 'render element' => 'element',
  138. ),
  139. // Provides a marker to highlight that the component is required.
  140. 'name_component_required_marker' => array(
  141. 'variables' => array('base_element' => NULL, 'component_key' => NULL, 'components' => NULL),
  142. ),
  143. // Provides the help for the recognized characters in the name_format()
  144. // format parameter.
  145. 'name_format_parameter_help' => array(
  146. 'file' => 'includes/name.parser.inc',
  147. ),
  148. );
  149. return $theme;
  150. }
  151. /**
  152. * Wrapper theming function for name_format().
  153. */
  154. function theme_name_item($variables) {
  155. $variables['settings'] += array(
  156. 'markup' => 0,
  157. );
  158. $format = name_get_format_by_machine_name($variables['format']);
  159. if (empty($format)) {
  160. $format = name_get_format_by_machine_name('default');
  161. }
  162. return name_format($variables['item'], $format, $variables['settings']);
  163. }
  164. /**
  165. * Renders a name component value.
  166. *
  167. * This function does not by default sanatise the output unless the markup
  168. * flag is set. If this is set, it runs the component through check_plain() and
  169. * wraps the component in a span with the component name set as the class.
  170. */
  171. function name_render_component($value, $component_key, $markup, $modifier = NULL) {
  172. if (empty($value) || !strlen($value)) {
  173. return NULL;
  174. }
  175. switch ($modifier) {
  176. case 'initial':
  177. $value = drupal_substr($value, 0, 1);
  178. break;
  179. }
  180. if ($markup) {
  181. return '<span class="' . check_plain($component_key) . '">' . check_plain($value) . '</span>';
  182. }
  183. return $value;
  184. }
  185. /**
  186. * Returns HTML for a marker for required name components.
  187. *
  188. * @param $variables
  189. * An associative array containing:
  190. * - element: An associative array containing the properties of the component.
  191. *
  192. * @ingroup themeable
  193. */
  194. function theme_name_component_required_marker($variables) {
  195. $base_element = $variables['base_element'];
  196. $components = $variables['components'];
  197. $component_key = $variables['component_key'];
  198. $name_translations = _name_translations();
  199. $title = empty($base_element['#title']) ? t('Name') : $base_element['#title'];
  200. if (!empty($base_element['#allow_family_or_given']) && ($component_key == 'given' || $component_key == 'family')) {
  201. $title_attribute = t('!given_title or !family_title is required when entering a !title.', array(
  202. '!given_title' => empty($components['given']['title']) ? $name_translations['given'] : $components['given']['title'],
  203. '!family_title' => empty($components['family']['title']) ? $name_translations['family'] : $components['family']['title'],
  204. '!title' => $title,
  205. ));
  206. }
  207. else {
  208. $component_title = empty($components[$component_key]['title']) ? $name_translations[$component_key] : $components[$component_key]['title'];
  209. $title_attribute = t('!component_title is required when entering a !title.', array('!component_title' => $component_title, '!title' => $title));
  210. }
  211. // Both field label and component labels have already been sanitized.
  212. return ' <span class="name-required-component-marker" title="' . $title_attribute . '">' . variable_get('name_component_required_marker', '*') . '</span>';
  213. }
  214. /**
  215. * Implements hook_username_alter().
  216. */
  217. function name_username_alter(&$name, $account) {
  218. // Don't alter anonymous users or objects that do not have any user ID.
  219. if (empty($account->uid)) {
  220. return;
  221. }
  222. // Try and load the realname in case this is a partial user object or
  223. // another object, such as a node or comment.
  224. if (!isset($account->realname)) {
  225. name_username_alter_preload($account);
  226. }
  227. if (isset($account->realname) && drupal_strlen($account->realname)) {
  228. $name = $account->realname;
  229. }
  230. }
  231. /**
  232. * Internal helper function to load the user account if required.
  233. *
  234. * Recursion check in place after RealName module issue queue suggested that
  235. * there were issues with token based recursion on load.
  236. */
  237. function name_username_alter_preload(&$account) {
  238. static $in_preload = FALSE;
  239. if (!$in_preload && !isset($account->realname)) {
  240. $field_name = variable_get('name_user_preferred', FALSE);
  241. if ($field_name && $instance = field_info_instance('user', $field_name, 'user')) {
  242. $in_preload = TRUE;
  243. $account = user_load($account->uid);
  244. $in_preload = FALSE;
  245. }
  246. }
  247. }
  248. /**
  249. * Implements hook_user_load().
  250. */
  251. function name_user_load(array $accounts) {
  252. // In the event there are a lot of user_load() calls, cache the results.
  253. $names = &drupal_static(__FUNCTION__, array());
  254. $field_name = variable_get('name_user_preferred', FALSE);
  255. if ($field_name && $instance = field_info_instance('user', $field_name, 'user')) {
  256. $format = name_get_format_by_machine_name($instance['settings']['override_format']);
  257. if (empty($format)) {
  258. $format = name_get_format_by_machine_name('default');
  259. }
  260. foreach ($accounts as $uid => $acccount) {
  261. if (isset($names[$uid])) {
  262. $accounts[$uid]->realname = $names[$uid];
  263. }
  264. else {
  265. if ($items = field_get_items('user', $acccount, $instance['field_name'])) {
  266. // We still have raw user input here.
  267. $items[0] += name_field_parse_additional_components('user', $acccount, $instance);
  268. $accounts[$uid]->realname = name_format($items[0], $format, array('object' => $acccount, 'type' => 'user'));
  269. $names[$uid] = $accounts[$uid]->realname;
  270. }
  271. }
  272. }
  273. }
  274. }
  275. /**
  276. * Loads a list of all user defined formats.
  277. */
  278. function name_get_custom_formats() {
  279. static $formats;
  280. if (!isset($formats)) {
  281. $formats = array();
  282. $result = db_query("SELECT * FROM {name_custom_format} ORDER BY name ASC");
  283. foreach ($result as $row) {
  284. $formats[$row->ncfid] = $row;
  285. }
  286. }
  287. return $formats;
  288. }
  289. /**
  290. * Helper function to generate a list of all defined custom formatting options.
  291. */
  292. function name_get_custom_format_options() {
  293. $options = array();
  294. foreach (name_get_custom_formats() as $wcfid => $row) {
  295. $options[$row->machine_name] = $row->name;
  296. }
  297. natcasesort($options);
  298. return $options;
  299. }
  300. /**
  301. * Loads a format based on the machine name.
  302. *
  303. * @param string $machine_name
  304. *
  305. * @return string
  306. * The name format.
  307. */
  308. function name_get_format_by_machine_name($machine_name) {
  309. static $drupal_static_fast;
  310. if (!isset($drupal_static_fast)) {
  311. $drupal_static_fast['formats'] = &drupal_static(__FUNCTION__);
  312. }
  313. $formats = &$drupal_static_fast['formats'];
  314. if (!isset($formats[$machine_name])) {
  315. if ($machine_name == 'default') {
  316. $formats[$machine_name] = name_settings('default_format');
  317. }
  318. else {
  319. $formats[$machine_name] = (string) db_query("SELECT format FROM {name_custom_format} WHERE machine_name = :machine_name", array(':machine_name' => $machine_name))->fetchField();
  320. }
  321. }
  322. return $formats[$machine_name];
  323. }
  324. /**
  325. * Static cache to reuse translated name components.
  326. *
  327. * These have double encoding to allow easy and targeted string overrides in the
  328. * sites settings.php file.
  329. */
  330. function _name_translations($intersect = NULL) {
  331. static $nt = NULL;
  332. if (!isset($nt)) {
  333. $nt = variable_get('name_field_part_overrides', 0);
  334. if (!$nt) {
  335. $nt = array(
  336. 'title' => t('!name_title', array('!name_title' => t('Title'))),
  337. 'given' => t('!name_given', array('!name_given' => t('Given'))),
  338. 'middle' => t('!name_middle', array('!name_middle' => t('Middle name(s)'))),
  339. 'family' => t('!name_family', array('!name_family' => t('Family'))),
  340. 'generational' => t('!name_generational', array('!name_generational' => t('Generational'))),
  341. 'credentials' => t('!name_credentials', array('!name_credentials' => t('Credentials'))),
  342. );
  343. }
  344. }
  345. return empty($intersect) ? $nt : array_intersect_key($nt, $intersect);
  346. }
  347. /**
  348. * Private helper function to define the formatter types that are available for
  349. * the CCK and Token modules.
  350. */
  351. function _name_formatter_output_types() {
  352. static $ot = NULL;
  353. if (!isset($ot)) {
  354. return array(
  355. 'default' => t('Default'),
  356. 'plain' => t('Plain'),
  357. 'raw' => t('Raw'),
  358. );
  359. }
  360. return $ot;
  361. }
  362. /* ------------------------- WebForm integration ---------------------------- */
  363. /**
  364. * Implements hook_webform_component_info().
  365. */
  366. function name_webform_component_info() {
  367. return array(
  368. 'name' => array(
  369. 'label' => t('Name'),
  370. 'description' => t('Defines a multi-value field for a persons name.'),
  371. 'features' => array(
  372. 'email_name' => TRUE,
  373. // @todo Maybe allow?
  374. 'conditional' => FALSE,
  375. // @todo More likely to trigger false hits?
  376. 'spam_analysis' => FALSE,
  377. ),
  378. 'file' => 'includes/webform.components.inc',
  379. ),
  380. );
  381. }
  382. /* ------------------------- FAPI Element Code ------------------------------ */
  383. /**
  384. * Implements hook_element_info().
  385. */
  386. function name_element_info() {
  387. $parts = _name_translations();
  388. $field_info = name_field_info();
  389. $field_settings = $field_info['name']['settings'];
  390. $instance_settings = $field_info['name']['instance_settings'];
  391. return array(
  392. 'name_element' => array(
  393. '#input' => TRUE,
  394. '#process' => array('name_element_expand'),
  395. '#pre_render' => array('name_element_pre_render'),
  396. '#element_validate' => array('name_element_validate'),
  397. '#theme_wrappers' => array('form_element'),
  398. '#theme' => 'name_element',
  399. '#show_component_required_marker' => 0,
  400. '#default_value' => array(
  401. 'title' => '',
  402. 'given' => '',
  403. 'middle' => '',
  404. 'family' => '',
  405. 'generational' => '',
  406. 'credentials' => '',
  407. ),
  408. '#minimum_components' => $field_settings['minimum_components'],
  409. '#allow_family_or_given' => $field_settings['allow_family_or_given'],
  410. '#components' => array(
  411. 'title' => array(
  412. 'type' => $instance_settings['title_field'],
  413. 'title' => $parts['title'],
  414. 'title_display' => 'description',
  415. 'inline_css' => isset($instance_settings['inline_css']['title']) ? $instance_settings['inline_css']['title'] : NULL,
  416. 'size' => isset($instance_settings['size']['title']) ? $instance_settings['size']['title'] : NULL,
  417. 'maxlength' => $field_settings['max_length']['title'],
  418. 'options' => $field_settings['title_options'],
  419. 'autocomplete' => FALSE,
  420. ),
  421. 'given' => array(
  422. 'type' => 'textfield',
  423. 'title' => $parts['given'],
  424. 'title_display' => 'description',
  425. 'inline_css' => isset($instance_settings['inline_css']['given']) ? $instance_settings['inline_css']['given'] : NULL,
  426. 'size' => isset($instance_settings['size']['given']) ? $instance_settings['size']['given'] : NULL,
  427. 'maxlength' => $field_settings['max_length']['given'],
  428. 'autocomplete' => FALSE,
  429. ),
  430. 'middle' => array(
  431. 'type' => 'textfield',
  432. 'title' => $parts['middle'],
  433. 'title_display' => 'description',
  434. 'inline_css' => isset($instance_settings['inline_css']['middle']) ? $instance_settings['inline_css']['middle'] : NULL,
  435. 'size' => isset($instance_settings['size']['middle']) ? $instance_settings['size']['middle'] : NULL,
  436. 'maxlength' => $field_settings['max_length']['middle'],
  437. 'autocomplete' => FALSE,
  438. ),
  439. 'family' => array(
  440. 'type' => 'textfield',
  441. 'title' => $parts['family'],
  442. 'title_display' => 'description',
  443. 'inline_css' => isset($instance_settings['inline_css']['family']) ? $instance_settings['inline_css']['family'] : NULL,
  444. 'size' => isset($instance_settings['size']['family']) ? $instance_settings['size']['family'] : NULL,
  445. 'maxlength' => $field_settings['max_length']['family'],
  446. 'autocomplete' => FALSE,
  447. ),
  448. 'generational' => array(
  449. 'type' => $instance_settings['generational_field'],
  450. 'title' => $parts['generational'],
  451. 'title_display' => 'description',
  452. 'inline_css' => isset($instance_settings['inline_css']['generational']) ? $instance_settings['inline_css']['generational'] : NULL,
  453. 'size' => isset($instance_settings['size']['generational']) ? $instance_settings['size']['generational'] : NULL,
  454. 'maxlength' => $field_settings['max_length']['generational'],
  455. 'options' => $field_settings['generational_options'],
  456. 'autocomplete' => FALSE,
  457. ),
  458. 'credentials' => array(
  459. 'type' => 'textfield',
  460. 'title' => $parts['credentials'],
  461. 'title_display' => 'description',
  462. 'inline_css' => isset($instance_settings['inline_css']['credentials']) ? $instance_settings['inline_css']['credentials'] : NULL,
  463. 'size' => isset($instance_settings['size']['credentials']) ? $instance_settings['size']['credentials'] : NULL,
  464. 'maxlength' => $field_settings['max_length']['credentials'],
  465. 'autocomplete' => FALSE,
  466. ),
  467. ),
  468. ),
  469. );
  470. }
  471. /**
  472. * Custom theme callback for the name_element.
  473. */
  474. function theme_name_element($variables) {
  475. $element = $variables['element'];
  476. return drupal_render_children($element);
  477. }
  478. /**
  479. * Themes a list of names.
  480. *
  481. * Note: This function expects a list of sanitised name items.
  482. */
  483. function theme_name_item_list($variables) {
  484. $items = $variables['items'];
  485. if (empty($items)) {
  486. return '';
  487. }
  488. $original_count = count($items);
  489. if ($original_count == 1) {
  490. return array_pop($items);
  491. }
  492. $settings = $variables['settings'];
  493. $settings += array(
  494. // default, plain, or raw.
  495. 'output' => 'default',
  496. 'multiple_delimiter' => ', ',
  497. // And or symbol.
  498. 'multiple_and' => 'text',
  499. // contextual, always, never.
  500. 'multiple_delimiter_precedes_last' => 'never',
  501. 'multiple_el_al_min' => 3,
  502. 'multiple_el_al_first' => 1,
  503. );
  504. $delimiter = $settings['multiple_delimiter'];
  505. if ($settings['output'] == 'default') {
  506. $etal = t('<em>et al</em>', array(), array('context' => 'name'));
  507. $delimiter = check_plain($delimiter);
  508. }
  509. else {
  510. $etal = t('et al', array(), array('context' => 'name'));
  511. if ($settings['output'] == 'plain') {
  512. $delimiter = strip_tags($delimiter);
  513. }
  514. }
  515. $t_args = array(
  516. '!delimiter' => $delimiter,
  517. '!etal' => $etal,
  518. );
  519. if ($settings['multiple_el_al_min'] && ($original_count > $settings['multiple_el_al_min'])) {
  520. $limit = min(array($settings['multiple_el_al_min'], $settings['multiple_el_al_first']));
  521. $items = array_slice($items, 0, $limit);
  522. if (count($items) == 1) {
  523. $t_args['!name'] = $items[0];
  524. return t('!name !etal', $t_args);
  525. }
  526. else {
  527. $t_args['!names'] = implode($delimiter . ' ', $items);
  528. return t('!names!delimiter !etal', $t_args);
  529. }
  530. }
  531. else {
  532. if ($settings['multiple_and'] == 'inherit') {
  533. return implode($delimiter . ' ', $items);
  534. }
  535. $t_args['!lastname'] = array_pop($items);
  536. $t_args['!names'] = implode($delimiter . ' ', $items);
  537. if ($settings['multiple_and'] == 'text') {
  538. $t_args['!and'] = t('and', array(), array('context' => 'name'));
  539. }
  540. else {
  541. $t_args['!and'] = $settings['output'] == 'default' ? '&amp' : '&';
  542. }
  543. // Strange rule from http://citationstyles.org/downloads/specification.html.
  544. if (($settings['multiple_delimiter_precedes_last'] == 'contextual' && $original_count > 2)
  545. || $settings['multiple_delimiter_precedes_last'] == 'always') {
  546. return t('!names!delimiter !and !lastname', $t_args);
  547. }
  548. else {
  549. return t('!names !and !lastname', $t_args);
  550. }
  551. }
  552. }
  553. /**
  554. * The #process callback to create the element.
  555. */
  556. function name_element_expand($element, &$form_state, $complete_form) {
  557. global $language;
  558. $element['#tree'] = TRUE;
  559. if (empty($element['#value'])) {
  560. $element['#value'] = array();
  561. }
  562. $parts = _name_translations();
  563. $element['#value'] += array_combine(array_keys($parts), array_fill(0, count($parts), ''));
  564. $components = $element['#components'];
  565. $min_components = (array) $element['#minimum_components'];
  566. if (empty($element['#field'])) {
  567. $element['#field'] = isset($form_state['field']) ? $form_state['field'] : NULL;
  568. }
  569. // Provides an override on the inline CSS.
  570. if (!empty($element['#component_css'])) {
  571. if ($element['#component_css'] == '<none>') {
  572. $inline_css = '';
  573. }
  574. else {
  575. $inline_css = ' style="' . check_plain($element['#component_css']) . '"';
  576. }
  577. }
  578. else {
  579. // Language will be LANGUAGE_LTR (0) or LANGUAGE_RTL (1).
  580. if ($language->direction) {
  581. if ($style = variable_get('name_inline_styles_rtl', 'float: right; margin-left: 1em;')) {
  582. $inline_css = ' style="' . $style . '"';
  583. }
  584. }
  585. else {
  586. if ($style = variable_get('name_inline_styles', 'float: left; margin-right: 1em;')) {
  587. $inline_css = ' style="' . $style . '"';
  588. }
  589. }
  590. }
  591. foreach ($parts as $key => $title) {
  592. if (!isset($components[$key]['exclude'])) {
  593. $element[$key] = _name_render_component($components, $key, $element, isset($min_components[$key]));
  594. $class = 'name-' . $key . '-wrapper';
  595. if ($key == 'credentials' && empty($element['#credentials_inline'])) {
  596. $element[$key]['#prefix'] = '<div class="' . $class
  597. . ' clearfix" style="float: ' . ($language->direction ? 'right' : 'left')
  598. . ';">';
  599. $element[$key]['#suffix'] = '</div>';
  600. }
  601. else {
  602. $element[$key]['#prefix'] = '<div class="' . $class . '"' . $inline_css . '>';
  603. $element[$key]['#suffix'] = '</div>';
  604. }
  605. }
  606. }
  607. if (variable_get('name_element_wrapper', 1)) {
  608. $element['#prefix'] = '<div class="clearfix">';
  609. $element['#suffix'] = '</div>';
  610. }
  611. return $element;
  612. }
  613. /**
  614. * Helper function to render a component within a name element.
  615. *
  616. * @param array $components
  617. * Core properties for all components.
  618. * @param string $component_key
  619. * The component key of the component that is being rendered.
  620. * @param array $base_element
  621. * Base FAPI element that makes up a name element.
  622. * @param bool $core
  623. * Flag that indicates that the component is required as part of a valid
  624. * name.
  625. *
  626. * @return array
  627. * The constructed component FAPI structure for a name element.
  628. */
  629. function _name_render_component($components, $component_key, $base_element, $core) {
  630. $component = $components[$component_key];
  631. $element = array();
  632. // Allow other modules to append additional FAPI properties to the element.
  633. foreach (element_properties($component) as $key) {
  634. $element[$key] = $component[$key];
  635. }
  636. $element['#attributes']['class'][] = 'name-element';
  637. $element['#attributes']['class'][] = 'name-' . $component_key;
  638. if ($core) {
  639. $element['#attributes']['class'][] = 'name-core-component';
  640. }
  641. if (isset($component['attributes'])) {
  642. foreach ($component['attributes'] as $key => $attribute) {
  643. if (isset($element['#attributes'][$key])) {
  644. if (is_array($attribute)) {
  645. $element['#attributes'][$key] = array_merge($element['#attributes'][$key], $attribute);
  646. }
  647. else {
  648. $element['#attributes'][$key] .= ' ' . $attribute;
  649. }
  650. }
  651. else {
  652. $element['#attributes'][$key] = $attribute;
  653. }
  654. }
  655. }
  656. $base_attributes = array('type', 'title', 'size', 'maxlength');
  657. foreach ($base_attributes as $key) {
  658. $element['#' . $key] = $component[$key];
  659. }
  660. $element['#default_value'] = $base_element['#value'][$component_key];
  661. if ($component['type'] == 'select') {
  662. $element['#options'] = $component['options'];
  663. $element['#size'] = 1;
  664. }
  665. elseif (!empty($component['autocomplete'])) {
  666. $element['#autocomplete_path'] = $component['autocomplete'];
  667. }
  668. if ($core && !empty($base_element['#show_component_required_marker'])) {
  669. $element['#title'] .= theme('name_component_required_marker', array(
  670. 'components' => $components,
  671. 'component_key' => $component_key,
  672. 'base_element' => $base_element,
  673. ));
  674. }
  675. // Enable the title options.
  676. $title_display = isset($component['title_display']) ? $component['title_display'] : 'description';
  677. switch ($title_display) {
  678. case 'title':
  679. break;
  680. case 'placeholder':
  681. $element['#attributes']['placeholder'] = t($element['#title']);
  682. $element['#title_display'] = 'invisible';
  683. break;
  684. case 'none':
  685. $element['#title_display'] = 'invisible';
  686. break;
  687. case 'description':
  688. default:
  689. $element['#title_display'] = 'invisible';
  690. $element['#description'] = t($element['#title']);
  691. break;
  692. }
  693. return $element;
  694. }
  695. /**
  696. * A custom validator to check the components of a name_element element.
  697. */
  698. function name_element_validate($element, &$form_state) {
  699. // Limits validation to posted values only.
  700. if (empty($element['#needs_validation'])) {
  701. return $element;
  702. }
  703. $minimum_components = array_filter($element['#minimum_components']);
  704. $labels = array();
  705. foreach ($element['#components'] as $key => $component) {
  706. if (!isset($component['exclude'])) {
  707. $labels[$key] = $component['title'];
  708. }
  709. }
  710. $item = $element['#value'];
  711. $empty = name_field_is_empty($item, NULL);
  712. $item_components = array();
  713. foreach (_name_translations() as $key => $title) {
  714. if (isset($labels[$key]) && !empty($item[$key])) {
  715. $item_components[$key] = 1;
  716. }
  717. }
  718. // Conditionally allow either a single given or family name.
  719. if (!empty($element['#allow_family_or_given'])) {
  720. // This option is only valid if there are both components.
  721. if (isset($labels['given']) && isset($labels['family'])) {
  722. if (!empty($item['given']) || !empty($item['family'])) {
  723. $item_components['given'] = 1;
  724. $item_components['family'] = 1;
  725. }
  726. }
  727. }
  728. if (!$empty && (count($minimum_components) != count(array_intersect_key($minimum_components, $item_components)))) {
  729. $missing_labels = array_diff_key(array_intersect_key($labels, $minimum_components), $item_components);
  730. $label = empty($element['#title']) ? (empty($element['#label']) ? 'Field' : $element['#label']) : $element['#title'];
  731. // Note that field_default_form() has already sanitized #title.
  732. form_error($element[key($missing_labels)], t('!name also requires the following parts: <em>!components</em>.',
  733. array('!name' => $label, '!components' => implode(', ', $missing_labels))));
  734. }
  735. if ($empty && $element['#required']) {
  736. form_error($element, t('<em>!name</em> is required.', array('!name' => t($element['#title']))));
  737. }
  738. return $element;
  739. }
  740. /**
  741. * This function themes the element and controls the title display.
  742. */
  743. function name_element_pre_render($element) {
  744. // Add a wrapper to fields if required.
  745. if (variable_get('name_element_wrapper', 1)) {
  746. $element['_name'] = array(
  747. '#prefix' => '<div class="clearfix">',
  748. '#suffix' => '</div>',
  749. );
  750. }
  751. foreach (_name_translations() as $key => $title) {
  752. if (isset($element[$key])) {
  753. $element['_name'][$key] = $element[$key];
  754. unset($element[$key]);
  755. }
  756. }
  757. if (!empty($element['#component_layout'])) {
  758. _name_component_layout($element['_name'], $element['#component_layout']);
  759. }
  760. if (!empty($element['#description'])) {
  761. $field = isset($element['#field']) ? $element['#field'] : NULL;
  762. if (!$field || $field['cardinality'] != 1) {
  763. $element['_description'] = array(
  764. '#value' => '<div class="clearfix description">' . $element['#description'] . '</div>',
  765. );
  766. unset($element['#description']);
  767. }
  768. }
  769. return $element;
  770. }
  771. /**
  772. * Sorts the widgets according to the language type.
  773. */
  774. function _name_component_layout(&$element, $layout = 'default') {
  775. $weights = array(
  776. 'asian' => array(
  777. 'family' => 1,
  778. 'middle' => 2,
  779. 'given' => 3,
  780. 'title' => 4,
  781. // 'generational' is removed from the display
  782. 'generational' => 5,
  783. 'credentials' => 6,
  784. ),
  785. 'eastern' => array(
  786. 'title' => 1,
  787. 'family' => 2,
  788. 'given' => 3,
  789. 'middle' => 4,
  790. // 'generational' is removed from the display
  791. 'generational' => 5,
  792. 'credentials' => 6,
  793. ),
  794. );
  795. if (isset($weights[$layout])) {
  796. foreach ($weights[$layout] as $component => $weight) {
  797. if (isset($element[$component])) {
  798. $element[$component]['#weight'] = $weight;
  799. }
  800. }
  801. }
  802. if ($layout == 'asian') {
  803. if (isset($element['generational'])) {
  804. $element['generational']['#default_value'] = '';
  805. $element['generational']['#access'] = FALSE;
  806. }
  807. }
  808. }
  809. /* ----------------------------- Field Code --------------------------------- */
  810. /**
  811. * Implements hook_field_info().
  812. */
  813. function name_field_info() {
  814. $parts = _name_translations();
  815. $keys = array_keys($parts);
  816. $count = count($keys);
  817. return array(
  818. 'name' => array(
  819. 'label' => t('Name'),
  820. 'description' => t('This field stores a users title, given, middle, family name, generational suffix and credentials in the database.'),
  821. 'default_widget' => 'name_widget',
  822. 'default_formatter' => 'name_formatter',
  823. 'settings' => array(
  824. // Components used. At least given or family name is required.
  825. 'components' => drupal_map_assoc($keys),
  826. // Minimal set of components before considered incomplete.
  827. 'minimum_components' => array(
  828. 'given' => 'given',
  829. 'family' => 'family',
  830. ),
  831. // Allows a given or family component value to filful either both the
  832. // minimal components. Allows singular names like "Prince".
  833. 'allow_family_or_given' => 0,
  834. 'labels' => _name_translations(),
  835. 'max_length' => array(
  836. 'title' => 31,
  837. 'given' => 63,
  838. 'middle' => 127,
  839. 'family' => 63,
  840. 'generational' => 15,
  841. 'credentials' => 255,
  842. ),
  843. 'autocomplete_source' => array(
  844. 'title' => array('title'),
  845. 'given' => array(),
  846. 'middle' => array(),
  847. 'family' => array(),
  848. 'generational' => array('generation'),
  849. 'credentials' => array(),
  850. ),
  851. 'autocomplete_separator' => array(
  852. 'title' => ' ',
  853. 'given' => ' -',
  854. 'middle' => ' -',
  855. 'family' => ' -',
  856. 'generational' => ' ',
  857. 'credentials' => ', ',
  858. ),
  859. 'title_options' => implode("\n", array(t('-- --'), t('Mr.'), t('Mrs.'), t('Miss'), t('Ms.'), t('Dr.'), t('Prof.'))),
  860. 'generational_options' => implode("\n", array(t('-- --'), t('Jr.'), t('Sr.'), t('I'), t('II'), t('III'), t('IV'), t('V'), t('VI'), t('VII'), t('VIII'), t('IX'), t('X'))),
  861. 'sort_options' => array(
  862. 'title' => 'title',
  863. 'generational' => 0,
  864. ),
  865. ),
  866. 'instance_settings' => array(
  867. // Possible elements for either (free) text, autocomplete, select.
  868. 'title_field' => 'select',
  869. 'generational_field' => 'select',
  870. 'size' => array(
  871. 'title' => 6,
  872. 'given' => 20,
  873. 'middle' => 20,
  874. 'family' => 20,
  875. 'generational' => 5,
  876. 'credentials' => 35,
  877. ),
  878. 'inline_css' => array_combine($keys, array_fill(0, $count, '')),
  879. 'title_display' => array_combine($keys, array_fill(0, $count, 'description')),
  880. // Field instance overrides.
  881. 'components' => array(),
  882. 'minimum_components' => array(),
  883. 'labels' => array_combine($keys, array_fill(0, $count, '')),
  884. // New settings to override the user name. Only applies to user entities.
  885. 'override_format' => 'default',
  886. 'preferred_field_reference' => '',
  887. // Prevents the default break between the name components and credentials.
  888. 'credentials_inline' => 0,
  889. 'component_css' => '',
  890. 'component_layout' => 'default',
  891. 'show_component_required_marker' => 0,
  892. ),
  893. 'property_type' => 'field_item_name',
  894. 'property_callbacks' => array('name_field_property_info_callback'),
  895. ),
  896. );
  897. }
  898. /**
  899. * Implements hook_field_settings_form().
  900. */
  901. function name_field_settings_form($field, $instance, $has_data) {
  902. module_load_include('inc', 'name', 'includes/name.content');
  903. return _name_field_settings_form($field, $instance, $has_data);
  904. }
  905. /**
  906. * Implements hook_field_settings_form_validate().
  907. *
  908. * Called via #element_validate from name_field_settings_form().
  909. */
  910. function name_field_settings_form_validate($form, &$form_state) {
  911. module_load_include('inc', 'name', 'includes/name.content');
  912. _name_field_settings_form_validate($form, $form_state);
  913. }
  914. /**
  915. * Implements hook_field_validate().
  916. *
  917. * @todo
  918. * Look at reintroducing this at some stage. Currently, this is problematic as
  919. * Drupal appears to loss track of what fields are shown on the form and this
  920. * results in validation errors.
  921. */
  922. // @code
  923. //function name_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
  924. // // Hack to test the default settings form.
  925. // if (empty($entity) && empty($entity_type)) {
  926. // return;
  927. // }
  928. //
  929. // if ($instance['required']) {
  930. // if (is_array($items)) {
  931. // foreach ($items as $delta => $item) {
  932. // if (!name_field_is_empty($item, $field)) {
  933. // return;
  934. // }
  935. // }
  936. // }
  937. // $errors[$field['field_name']][$langcode][0][] = array(
  938. // 'error' => 'required_components',
  939. // 'message' => t('%name is required.', array('%name' => t($instance['label']))),
  940. // );
  941. // }
  942. //}
  943. // @endcode
  944. /**
  945. * Implements hook_field_load().
  946. */
  947. function name_field_load($entity_type, $entities, $field, $instances, $langcode, &$items) {
  948. foreach ($entities as $id => $entity) {
  949. // Use populated overrides if present.
  950. $instances[$id]['settings'] += array('components' => array());
  951. $components = name_get_instance_components($field['settings']['components'], $instances[$id]['settings']['components']);
  952. foreach ($items[$id] as $delta => $item) {
  953. foreach (_name_translations() as $key => $title) {
  954. if (empty($components[$key])) {
  955. unset($items[$id][$delta][$key]);
  956. }
  957. else {
  958. $items[$id][$delta]['safe'][$key] = _name_value_sanitize($item, $key);
  959. }
  960. }
  961. }
  962. }
  963. }
  964. /**
  965. * Implements hook_field_is_empty().
  966. */
  967. function name_field_is_empty($item, $field) {
  968. foreach (_name_translations() as $key => $title) {
  969. // Title & generational have no meaning by themselves.
  970. if ($key == 'title' || $key == 'generational') {
  971. continue;
  972. }
  973. if (!empty($item[$key])) {
  974. return FALSE;
  975. }
  976. }
  977. return TRUE;
  978. }
  979. /**
  980. * Checks that a component is not selected as a minimal component when the
  981. * component is not selected.
  982. */
  983. function _name_field_minimal_component_requirements($element, &$form_state) {
  984. $values = array_filter((array) $element['#value']);
  985. if ($values) {
  986. $required_field_set = array_flip(array('given', 'family'));
  987. $values = array_intersect_key($required_field_set, $values);
  988. if (empty($values)) {
  989. $required_fields = array_intersect_key(_name_translations(), $required_field_set);
  990. form_set_error(implode('][', $element['#parents']) . '][given]',
  991. t('!label must have one of the following components: %components',
  992. array('!label' => $element['#title'], '%components' => implode(', ', $required_fields))));
  993. }
  994. }
  995. }
  996. /* ----------------------------- Widget Code -------------------------------- */
  997. /**
  998. * Implements hook_field_widget_info().
  999. */
  1000. function name_field_widget_info() {
  1001. return array(
  1002. 'name_widget' => array(
  1003. 'label' => t('Name'),
  1004. 'field types' => array('name'),
  1005. 'behaviors' => array(
  1006. 'multiple values' => FIELD_BEHAVIOR_DEFAULT,
  1007. 'default value' => FIELD_BEHAVIOR_DEFAULT,
  1008. ),
  1009. 'settings' => array(),
  1010. ),
  1011. );
  1012. }
  1013. /**
  1014. * Implements hook_field_widget_error().
  1015. *
  1016. * Validation on required is done here. The max. length is done via Drupal.
  1017. * Min. components is done via name_element validation callback.
  1018. *
  1019. * @todo
  1020. * Re-add this with name_field_validate().
  1021. */
  1022. // @code
  1023. //function name_field_widget_error($element, $error, $form, &$form_state) {
  1024. // $error_element = NULL;
  1025. // switch ($error['error']) {
  1026. // case 'required_components':
  1027. // $error_element = $element[$element['#columns'][1]];
  1028. // break;
  1029. //
  1030. // }
  1031. // if ($error_element) {
  1032. // form_error($error_element, $error['message']);
  1033. // }
  1034. //}
  1035. // @endcode
  1036. /**
  1037. * Implements hook_field_widget_form().
  1038. */
  1039. function name_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  1040. module_load_include('inc', 'name', 'includes/name.content');
  1041. return _name_field_widget_form($form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
  1042. }
  1043. /**
  1044. * Implements hook_field_instance_settings_form().
  1045. */
  1046. function name_field_instance_settings_form($field, $instance) {
  1047. module_load_include('inc', 'name', 'includes/name.content');
  1048. return _name_field_instance_settings_form($field, $instance);
  1049. }
  1050. /**
  1051. * Name autocomplete data sources callback.
  1052. *
  1053. * Generates a full listing for title and generational options.
  1054. *
  1055. * @param array $field
  1056. * The field requesting the data.
  1057. *
  1058. * @param array $component
  1059. * The key that the data has been requested for.
  1060. *
  1061. * @param mixed $arguments
  1062. * Additional arguments defined in hook_name_data_sources().
  1063. *
  1064. * @return array
  1065. * The matches ready to use directly in the autocomplete response.
  1066. */
  1067. function name_field_get_options($field, $component = 'title', $arguments = array()) {
  1068. module_load_include('inc', 'name', 'includes/name.content');
  1069. return _name_field_get_options($field, $component, $arguments);
  1070. }
  1071. /**
  1072. * Name autocomplete sources query callback.
  1073. *
  1074. * Handles the autocomplete for title and generational options.
  1075. *
  1076. * @param array $components
  1077. * The keys that the autocomplete is active on.
  1078. *
  1079. * @param string $base_string
  1080. * Autocomplete strings are split and individual pieces are parsed
  1081. * sequentially. The base string is the concatanated results of
  1082. * previous segment lookups.
  1083. *
  1084. * @param string $string
  1085. * Current segment of the search string.
  1086. *
  1087. * @param array|null $field
  1088. * If run in the context of the Field API, the field is supplied.
  1089. *
  1090. * @param int $limit
  1091. *
  1092. * @param mixed $arguments
  1093. * Additional arguments defined in hook_name_data_sources().
  1094. *
  1095. * @return array
  1096. * The matches ready to use directly in the autocomplete response.
  1097. */
  1098. function name_field_autocomplete_query($components, $base_string, $string, $field, $limit, $arguments) {
  1099. $component_key = $arguments;
  1100. $matches = array();
  1101. foreach (name_field_get_options($field, $component_key) as $key => $option) {
  1102. if (strpos(drupal_strtolower($key), $string) === 0 || strpos(drupal_strtolower($option), $string) === 0) {
  1103. $matches[$base_string . $key] = $key;
  1104. $limit--;
  1105. }
  1106. if (!$limit) {
  1107. break;
  1108. }
  1109. }
  1110. return $matches;
  1111. }
  1112. /* ---------------------------- Formatter Code ------------------------------ */
  1113. /**
  1114. * Implements hook_field_formatter_info().
  1115. */
  1116. function name_field_formatter_info() {
  1117. $formatters = array();
  1118. $formatters['name_formatter'] = array(
  1119. 'label' => t('Default'),
  1120. 'field types' => array('name'),
  1121. 'settings' => array(
  1122. 'markup' => 0,
  1123. // default, plain, or raw.
  1124. 'output' => 'default',
  1125. 'format' => 'default',
  1126. 'multiple' => 'default',
  1127. // These define how the multiple list option is configured.
  1128. 'multiple_delimiter' => ', ',
  1129. // Text or symbol.
  1130. 'multiple_and' => 'text',
  1131. // contextual, always, never.
  1132. 'multiple_delimiter_precedes_last' => 'never',
  1133. 'multiple_el_al_min' => 3,
  1134. 'multiple_el_al_first' => 1,
  1135. ),
  1136. );
  1137. return $formatters;
  1138. }
  1139. /**
  1140. * Helper function to define the available output formatter options.
  1141. */
  1142. function _name_formatter_output_options() {
  1143. return array(
  1144. 'default' => t('Default'),
  1145. 'plain' => t('Plain text'),
  1146. 'raw' => t('Raw value (not recommended)'),
  1147. );
  1148. }
  1149. /**
  1150. * Helper function to define the available multiple formatter options.
  1151. */
  1152. function _name_formatter_multiple_options() {
  1153. return array(
  1154. 'default' => t('Default'),
  1155. 'inline_list' => t('Inline list'),
  1156. );
  1157. }
  1158. /**
  1159. * Implements hook_field_formatter_settings_form().
  1160. */
  1161. function name_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  1162. $display = $instance['display'][$view_mode];
  1163. $settings = $display['settings'];
  1164. $element = array();
  1165. $element['format'] = array(
  1166. '#type' => 'select',
  1167. '#title' => t('Name format'),
  1168. '#default_value' => isset($settings['format']) ? $settings['format'] : 'default',
  1169. '#options' => array('default' => t('Default')) + name_get_custom_format_options(),
  1170. '#required' => TRUE,
  1171. );
  1172. $element['markup'] = array(
  1173. '#type' => 'checkbox',
  1174. '#title' => t('Markup'),
  1175. '#default_value' => !empty($settings['markup']),
  1176. '#description' => t('This option wraps the individual components of the name in SPAN elements with corresponding classes to the component.'),
  1177. );
  1178. $element['output'] = array(
  1179. '#type' => 'radios',
  1180. '#title' => t('Output'),
  1181. '#default_value' => empty($settings['output']) ? 'default' : $settings['output'],
  1182. '#options' => _name_formatter_output_options(),
  1183. '#description' => t('This option provides additional options for rendering the field. <strong>Normally, using the "Raw value" option would be a security risk.</strong>'),
  1184. '#required' => TRUE,
  1185. );
  1186. $element['multiple'] = array(
  1187. '#type' => 'radios',
  1188. '#title' => t('Multiple format options'),
  1189. '#default_value' => empty($settings['multiple']) ? 'default' : $settings['multiple'],
  1190. '#options' => _name_formatter_multiple_options(),
  1191. '#required' => TRUE,
  1192. );
  1193. $settings += array(
  1194. 'multiple_delimiter' => ', ',
  1195. // And or symbol.
  1196. 'multiple_and' => 'text',
  1197. // contextual, always, never.
  1198. 'multiple_delimiter_precedes_last' => 'never',
  1199. 'multiple_el_al_min' => 3,
  1200. 'multiple_el_al_first' => 1,
  1201. );
  1202. $base = array(
  1203. '#states' => array(
  1204. 'visible' => array(
  1205. ':input[name="fields[' . $field['field_name'] . '][settings_edit_form][settings][multiple]"]' => array('value' => 'inline_list'),
  1206. ),
  1207. ),
  1208. '#prefix' => '<div style="padding: 0 2em;">',
  1209. '#suffix' => '</div>',
  1210. );
  1211. // We can not nest this field, so use a prefix / suffix with padding to help
  1212. // to provide context.
  1213. $element['multiple_delimiter'] = $base + array(
  1214. '#type' => 'textfield',
  1215. '#title' => t('Delimiter'),
  1216. '#default_value' => $settings['multiple_delimiter'],
  1217. '#description' => t('This specifies the delimiter between the second to last and the last name.'),
  1218. );
  1219. $element['multiple_and'] = $base + array(
  1220. '#type' => 'radios',
  1221. '#title' => t('Last delimiter type'),
  1222. '#options' => array(
  1223. 'text' => t('Textual (and)'),
  1224. 'symbol' => t('Ampersand (&amp;)'),
  1225. 'inherit' => t('Inherit'),
  1226. ),
  1227. '#default_value' => $settings['multiple_and'],
  1228. '#description' => t('This specifies the delimiter between the second to last and the last name.'),
  1229. );
  1230. $element['multiple_delimiter_precedes_last'] = $base + array(
  1231. '#type' => 'radios',
  1232. '#title' => t('Standard delimiter precedes last delimiter'),
  1233. '#options' => array(
  1234. 'never' => t('Never (i.e. "J. Doe and T. Williams")'),
  1235. 'always' => t('Always (i.e. "J. Doe<strong>,</strong> and T. Williams")'),
  1236. 'contextual' => t('Contextual (i.e. "J. Doe and T. Williams" <em>or</em> "J. Doe, S. Smith<strong>,</strong> and T. Williams")'),
  1237. ),
  1238. '#default_value' => $settings['multiple_delimiter_precedes_last'],
  1239. '#description' => t('This specifies the delimiter between the second to last and the last name. Contextual means that the delimiter is only included for lists with three or more names.'),
  1240. );
  1241. $element['multiple_el_al_min'] = $base + array(
  1242. '#type' => 'select',
  1243. '#title' => t('Reduce list and append <em>el al</em>'),
  1244. '#options' => array(0 => t('Never reduce')) + drupal_map_assoc(range(1, 20)),
  1245. '#default_value' => $settings['multiple_el_al_min'],
  1246. '#description' => t('This specifies a limit on the number of names to display. After this limit, names are removed and the abbrivation <em>et al</em> is appended. This Latin abbrivation of <em>et alii</em> means "and others".'),
  1247. );
  1248. $element['multiple_el_al_first'] = $base + array(
  1249. '#type' => 'select',
  1250. '#title' => t('Number of names to display when using <em>el al</em>'),
  1251. '#options' => drupal_map_assoc(range(1, 20)),
  1252. '#default_value' => $settings['multiple_el_al_first'],
  1253. );
  1254. return $element;
  1255. }
  1256. /**
  1257. * Implements hook_field_formatter_settings_summary().
  1258. */
  1259. function name_field_formatter_settings_summary($field, $instance, $view_mode) {
  1260. $display = $instance['display'][$view_mode];
  1261. $settings = $display['settings'];
  1262. $summary = array();
  1263. $machine_name = isset($settings['format']) ? $settings['format'] : 'default';
  1264. if ($machine_name == 'default') {
  1265. $summary[] = t('Format: Default');
  1266. }
  1267. else {
  1268. $info = db_select('name_custom_format', 'n')
  1269. ->fields('n')
  1270. ->condition('machine_name', $machine_name)
  1271. ->execute()
  1272. ->fetchObject();
  1273. if ($info) {
  1274. $summary[] = t('Format: %format (@machine_name)', array(
  1275. '%format' => $info->name,
  1276. '@machine_name' => $info->machine_name,
  1277. ));
  1278. }
  1279. else {
  1280. $summary[] = t('Format: <strong>Missing format.</strong><br/>This field will be displayed using the Default format.');
  1281. $machine_name = 'default';
  1282. }
  1283. }
  1284. // Provide an example of the selected format.
  1285. module_load_include('admin.inc', 'name');
  1286. $used_components = array_filter($field['settings']['components']);
  1287. $excluded_components = array_diff_key($used_components, _name_translations());
  1288. $examples = name_example_names($excluded_components, $field['field_name']);
  1289. if ($examples && $example = array_shift($examples)) {
  1290. $format = name_get_format_by_machine_name($machine_name);
  1291. $formatted = check_plain(name_format($example, $format));
  1292. if (empty($formatted)) {
  1293. $formatted = '<em>&lt;&lt;empty&gt;&gt;</em>';
  1294. }
  1295. $summary[] = t('Example: !example', array(
  1296. '!example' => $formatted,
  1297. ));
  1298. }
  1299. $summary[] = t('Markup: @yesno', array(
  1300. '@yesno' => empty($settings['markup']) ? t('no') : t('yes'),
  1301. ));
  1302. $output_options = _name_formatter_output_options();
  1303. $output = empty($settings['output']) ? 'default' : $settings['output'];
  1304. $summary[] = t('Output: @format', array(
  1305. '@format' => $output_options[$output],
  1306. ));
  1307. return implode('<br/>', $summary);
  1308. }
  1309. /**
  1310. * Implements hook_field_formatter_view().
  1311. */
  1312. function name_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  1313. $element = array();
  1314. $settings = $display['settings'];
  1315. $type = empty($settings['output']) ? 'default' : $settings['output'];
  1316. $format = isset($settings['format']) ? $settings['format'] : 'default';
  1317. $format = name_get_format_by_machine_name($format);
  1318. if (empty($format)) {
  1319. $format = name_get_format_by_machine_name('default');
  1320. }
  1321. foreach ($items as $delta => $item) {
  1322. $item += name_field_parse_additional_components($entity_type, $entity, $instance);
  1323. // We still have raw user input here unless the markup flag has been used.
  1324. $value = name_format($item, $format, array(
  1325. 'object' => $entity,
  1326. 'type' => $entity_type,
  1327. 'markup' => !empty($display['settings']['markup']),
  1328. ));
  1329. if (empty($display['settings']['markup'])) {
  1330. $element[$delta] = array(
  1331. '#markup' => _name_value_sanitize($value, NULL, $type),
  1332. );
  1333. }
  1334. else {
  1335. $element[$delta] = array('#markup' => $value);
  1336. }
  1337. }
  1338. if (isset($settings['multiple']) && $settings['multiple'] == 'inline_list') {
  1339. $items = array();
  1340. foreach (element_children($element) as $delta) {
  1341. if (!empty($element[$delta]['#markup'])) {
  1342. $items[] = $element[$delta]['#markup'];
  1343. unset($element[$delta]);
  1344. }
  1345. }
  1346. if (!empty($items)) {
  1347. $element[0]['#markup'] = theme('name_item_list', array('items' => $items, 'settings' => $settings));
  1348. }
  1349. }
  1350. return $element;
  1351. }
  1352. /**
  1353. * Helper function to get alternative name component sources.
  1354. */
  1355. function name_get_additional_sources($instance) {
  1356. $sources = array();
  1357. $info = entity_get_info();
  1358. $sources['_self'] = t('@label label', array(
  1359. '@label' => $info[$instance['entity_type']]['label'],
  1360. ));
  1361. if ($instance['entity_type'] == 'user') {
  1362. $sources['_self_property_name'] = t('@label login name', array(
  1363. '@label' => $info[$instance['entity_type']]['label'],
  1364. ));
  1365. }
  1366. foreach (field_info_instances($instance['entity_type'], $instance['bundle']) as $_field_name => $_instance) {
  1367. if ($_field_name != $instance['field_name']) {
  1368. $sources[$_field_name] = $_instance['label'];
  1369. }
  1370. }
  1371. return $sources;
  1372. }
  1373. /**
  1374. * Helper function to discover alternative name component sources.
  1375. */
  1376. function name_field_parse_additional_components($entity_type, $entity, $instance) {
  1377. $settings = $instance['settings'];
  1378. $extra = array();
  1379. $map = array(
  1380. 'preferred' => 'preferred_field_reference',
  1381. );
  1382. list(,, $bundle) = entity_extract_ids($entity_type, $entity);
  1383. foreach ($map as $component => $key) {
  1384. if (!empty($settings[$key])) {
  1385. if ($settings[$key] == '_self') {
  1386. if ($label = entity_label($entity_type, $entity)) {
  1387. $extra[$component] = $label;
  1388. }
  1389. }
  1390. elseif (strpos($settings[$key], '_self_property') === 0) {
  1391. $property = str_replace('_self_property_', '', $settings[$key]);
  1392. if (!empty($entity->{$property}) && is_scalar($entity->{$property})) {
  1393. $extra[$component] = $entity->{$property};
  1394. }
  1395. }
  1396. elseif ($_instance = field_info_instance($entity_type, $settings[$key], $bundle)) {
  1397. $_field = field_info_field($settings[$key]);
  1398. $_values = array();
  1399. if (field_access('view', $_field, $entity_type) || field_access('edit', $_field, $entity_type)) {
  1400. $clone = clone $entity;
  1401. $_langcode = field_language($entity_type, $entity, $settings[$key]);
  1402. // @todo
  1403. $_view_mode = 'default';
  1404. $_display = field_get_display($_instance, $_view_mode, $clone);
  1405. if ($_display['type'] == 'hidden') {
  1406. $cache = _field_info_field_cache();
  1407. $_display = array();
  1408. $_display = $cache->prepareInstanceDisplay($_display, $_field["type"]);
  1409. }
  1410. $_display['label'] = 'hidden';
  1411. $_element = field_view_field($entity_type, $clone, $settings[$key], $_display, $_langcode);
  1412. if ($output = trim(strip_tags(drupal_render($_element)))) {
  1413. $extra[$component] = decode_entities($output);
  1414. }
  1415. }
  1416. }
  1417. }
  1418. }
  1419. return $extra;
  1420. }
  1421. /**
  1422. * Helper function to sanitize a name component or name string.
  1423. *
  1424. * @param string $langcode
  1425. * @param mixed $item
  1426. * If this is a string, then the processing happens on this.
  1427. * If this is an array, the processing happens on the column index.
  1428. * @param string $column
  1429. * @param string $type
  1430. * Tells the function how to handle the text processing:
  1431. * 'default' runs through check_plain()
  1432. * 'plain' runs through strip_tags()
  1433. * 'raw' has no processing applied to it.
  1434. */
  1435. function _name_value_sanitize($item, $column = NULL, $type = 'default') {
  1436. $safe_key = 'safe' . ($type == 'default' ? '' : '_' . $type);
  1437. if (is_array($item) && isset($item[$safe_key])) {
  1438. return $item[$safe_key][$column];
  1439. }
  1440. $value = is_array($item) ? (string) $item[$column] : $item;
  1441. switch ($type) {
  1442. case 'plain':
  1443. return strip_tags($value);
  1444. case 'raw':
  1445. return $value;
  1446. default:
  1447. return check_plain($value);
  1448. }
  1449. }
  1450. /* -------------------------- FAPI Form Alters ------------------------------ */
  1451. /**
  1452. * Provides a hook into the theming of the field, instance and display settings,
  1453. * using #pre_render callbacks.
  1454. */
  1455. function name_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
  1456. $field = $form['#field'];
  1457. if ($field['module'] != 'name') {
  1458. return;
  1459. }
  1460. // Moves the instance settings into a nicer table.
  1461. if (isset($form['instance']['settings'])) {
  1462. $form['instance']['#pre_render'][] = 'name_field_instance_settings_pre_render';
  1463. }
  1464. // Moves the field settings into a nicer table.
  1465. if (isset($form['field']['settings'])) {
  1466. $form['field']['settings']['#pre_render'][] = 'name_field_settings_pre_render';
  1467. }
  1468. }
  1469. /**
  1470. *
  1471. */
  1472. function name_form_field_ui_field_settings_form_alter(&$form) {
  1473. if ($form['field']['module']['#value'] != 'name') {
  1474. return;
  1475. }
  1476. if (isset($form['field']['settings'])) {
  1477. $form['field']['settings']['#pre_render'][] = 'name_field_settings_pre_render';
  1478. }
  1479. }
  1480. /**
  1481. * A #pre_render function callback for formatting field instance settings.
  1482. */
  1483. function name_field_instance_settings_pre_render($form) {
  1484. module_load_include('inc', 'name', 'name.admin');
  1485. return _name_field_instance_settings_pre_render($form);
  1486. }
  1487. /**
  1488. * A #pre_render function callback for formatting field settings.
  1489. */
  1490. function name_field_settings_pre_render($form) {
  1491. module_load_include('inc', 'name', 'name.admin');
  1492. return _name_field_settings_pre_render($form);
  1493. }
  1494. /**
  1495. * Implements hook_field_create_instance().
  1496. */
  1497. function name_field_create_instance($instance) {
  1498. if ($instance['entity_type'] == 'user' && !empty($instance['settings']['name_user_preferred'])) {
  1499. variable_set('name_user_preferred', $instance['field_name']);
  1500. }
  1501. }
  1502. /**
  1503. * Implements hook_field_update_instance().
  1504. */
  1505. function name_field_update_instance($instance, $prior_instance) {
  1506. // I'm only targeting user entities here.
  1507. if ($instance['entity_type'] == 'user') {
  1508. if (!empty($instance['settings']['name_user_preferred'])) {
  1509. variable_set('name_user_preferred', $instance['field_name']);
  1510. }
  1511. elseif (!empty($prior_instance['settings']['name_user_preferred'])) {
  1512. variable_set('name_user_preferred', '');
  1513. }
  1514. }
  1515. }
  1516. /**
  1517. * Implements hook_field_delete_instance().
  1518. */
  1519. function name_field_delete_instance($instance) {
  1520. if ($instance['entity_type'] == 'user' && !empty($instance['settings']['name_user_preferred'])) {
  1521. variable_set('name_user_preferred', '');
  1522. }
  1523. }
  1524. /* -------------------------- namedb integration ---------------------------- */
  1525. /**
  1526. * Implements hook_field_insert().
  1527. *
  1528. * Triggers a custom hook for name field inserts. Field API only calls the
  1529. * module that defines the field.
  1530. */
  1531. function name_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
  1532. // Ensure that $items are passed by reference.
  1533. foreach (module_implements('name_field_insert_notification') as $module) {
  1534. $function = $module . '_name_field_insert_notification';
  1535. $function($entity_type, $entity, $field, $instance, $langcode, $items);
  1536. }
  1537. }
  1538. /**
  1539. * Implements hook_field_update().
  1540. *
  1541. * Triggers a custom hook for name field updates. Field API only calls the
  1542. * module that defines the field.
  1543. */
  1544. function name_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
  1545. // Ensure that $items are passed by reference.
  1546. foreach (module_implements('name_field_update_notification') as $module) {
  1547. $function = $module . '_name_field_insert_notification';
  1548. $function($entity_type, $entity, $field, $instance, $langcode, $items);
  1549. }
  1550. }
  1551. /**
  1552. * Helper function to normalise the word for sorting.
  1553. *
  1554. * @todo Better functions in PHP?
  1555. *
  1556. * All credit goes to the PathAuto module for this!
  1557. */
  1558. function name_translite_word($word) {
  1559. static $translations;
  1560. if (!isset($translations)) {
  1561. $file = drupal_get_path('module', 'name') . '/' . 'i18n-ascii.txt';
  1562. $translations = parse_ini_file($file);
  1563. }
  1564. return strtr($word, $translations);
  1565. }
  1566. /**
  1567. * Helper function to get all of the allowed letters in a name.
  1568. */
  1569. function name_translite_letters($base = 'a-zA-Z') {
  1570. static $letters;
  1571. if (!isset($letters)) {
  1572. $file = drupal_get_path('module', 'name') . '/' . 'i18n-ascii.txt';
  1573. $letters = '';
  1574. $translations = parse_ini_file($file);
  1575. foreach ($translations as $l => $t) {
  1576. $letters .= $l;
  1577. }
  1578. }
  1579. return $base . $letters;
  1580. }
  1581. /**
  1582. * Checks that the name matches a reasonable format before saving it to the
  1583. * database.
  1584. *
  1585. * The valid list so far is:
  1586. *
  1587. * xxx > Xxx
  1588. * d'xxx > D'Xxx
  1589. * l'xxx > L'Xxx
  1590. * m'xxx > M'Xxx
  1591. * o'xxx > O'Xxx
  1592. * de|del|des|di|du|la|le|van|von xxx > De Xxx
  1593. * de la xxx > De la Xxx
  1594. * van|von de|den|der xxx > Van Xxx
  1595. * st|st.|staint xxx > Staint Xxx
  1596. * van der xxx > Van der Xxx
  1597. * mc|mac xxx > McXxx
  1598. *
  1599. * Ignored list includes:
  1600. *
  1601. * xxx xxx
  1602. * etc
  1603. *
  1604. * Configurable options:
  1605. *
  1606. * xxx-xxx
  1607. * x
  1608. *
  1609. * Note that this is an API change from Name Field 7.x-1.4 to 7.x-1.5 that was
  1610. * required after implementing theme_name_component(). The previous version of
  1611. * this function was name_process_name_component().
  1612. */
  1613. function name_clean_name_component($name, $data_key = 'user_data', $letters = 'a-z') {
  1614. $name = trim($name);
  1615. $settings = namedb_settings();
  1616. $min = intval($settings[$data_key]['minimum_length']);
  1617. if ($min <= 0) {
  1618. $min = 1;
  1619. }
  1620. if (empty($name) || strpos($name, '-') || drupal_strlen($name) < $min) {
  1621. return FALSE;
  1622. }
  1623. if (preg_match('/^de la ([' . $letters . ']{' . $min . ',})$/i', $name, $match)) {
  1624. return 'De la ' . drupal_ucfirst($match[1]);
  1625. }
  1626. elseif (preg_match('/^(van|von) (de|den|der) ([' . $letters . ']{' . $min . ',})$/i', $name, $match)) {
  1627. return drupal_ucfirst(drupal_strtolower($match[1])) . ' ' . drupal_strtolower($match[2]) . ' ' . drupal_ucfirst($match[3]);
  1628. }
  1629. elseif (preg_match('/^(st\.|st|staint) ([' . $letters . ']{' . $min . ',})$/i', $name, $match)) {
  1630. return 'Staint ' . drupal_ucfirst($match[2]);
  1631. }
  1632. elseif (preg_match('/^(d|l|m|o) *\' *([' . $letters . ']{' . $min . ',})$/i', $name, $match)) {
  1633. return drupal_strtoupper($match[1]) . "'" . drupal_ucfirst($match[2]);
  1634. }
  1635. elseif (preg_match('/^(de|del|des|di|du|la|le|van|von) ([' . $letters . ']{' . $min . ',})$/i', $name, $match)) {
  1636. return drupal_ucfirst(drupal_strtolower($match[1])) . ' ' . drupal_ucfirst($match[2]);
  1637. }
  1638. elseif (preg_match('/^(mc|mac) ([' . $letters . ']{' . $min . ',})$/i', $name, $match)) {
  1639. return drupal_ucfirst(drupal_strtolower($match[1])) . drupal_ucfirst($match[2]);
  1640. }
  1641. elseif (preg_match('/^([' . $letters . ']{' . $min . ',}) de ([' . $letters . ']{' . $min . ',})$/i', $name, $match)) {
  1642. return drupal_ucfirst(drupal_strtolower($match[1])) . ' de ' . drupal_ucfirst($match[2]);
  1643. }
  1644. elseif (preg_match('/^[' . $letters . ']{' . $min . ',}$/i', $name)) {
  1645. return drupal_ucfirst($name);
  1646. }
  1647. if ($settings[$data_key]['hyphenated']) {
  1648. if (preg_match('/^([' . $letters . ']{' . $min . ',}) *\- *([' . $letters . ']{' . $min . ',})$/i', $name)) {
  1649. return drupal_ucfirst($match[1]) . '-' . drupal_ucfirst($match[2]);
  1650. }
  1651. }
  1652. return FALSE;
  1653. }
  1654. /**
  1655. * Implements hook_views_api().
  1656. */
  1657. function name_views_api() {
  1658. return array('version' => '3.0');
  1659. }
  1660. /**
  1661. * Additional callback to adapt the property info of name fields.
  1662. *
  1663. * @see entity_metadata_field_entity_property_info()
  1664. */
  1665. function name_field_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
  1666. $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']];
  1667. $property['getter callback'] = 'entity_metadata_field_verbatim_get';
  1668. $property['setter callback'] = 'entity_metadata_field_verbatim_set';
  1669. // Auto-create the field item as soon as a property is set.
  1670. $property['auto creation'] = 'name_field_item_create';
  1671. $property['property info'] = name_field_item_property_info();
  1672. foreach ($property['property info'] as $property_key => $property_info) {
  1673. $property['property info'][$property_key]['required'] = !empty($field['settings']['minimun_components'][$property_key]);
  1674. if (empty($field['settings']['components'][$property_key])) {
  1675. unset($property['property info'][$property_key]);
  1676. }
  1677. }
  1678. unset($property['query callback']);
  1679. }
  1680. /**
  1681. * Callback for creating a new, empty name fields item.
  1682. *
  1683. * @see name_field_property_info_callback()
  1684. */
  1685. function name_field_item_create() {
  1686. return array(
  1687. 'title' => NULL,
  1688. 'given' => NULL,
  1689. 'middle' => NULL,
  1690. 'family' => NULL,
  1691. 'generational' => NULL,
  1692. 'credentials' => NULL,
  1693. );
  1694. }
  1695. /**
  1696. * Defines info for the properties of the name-field item data structure.
  1697. */
  1698. function name_field_item_property_info() {
  1699. $properties['title'] = array(
  1700. 'type' => 'text',
  1701. 'label' => t('The title of the name.'),
  1702. 'setter callback' => 'entity_property_verbatim_set',
  1703. );
  1704. $properties['given'] = array(
  1705. 'type' => 'text',
  1706. 'label' => t('The given name.'),
  1707. 'setter callback' => 'entity_property_verbatim_set',
  1708. );
  1709. $properties['middle'] = array(
  1710. 'type' => 'text',
  1711. 'label' => t('The middle of the name.'),
  1712. 'setter callback' => 'entity_property_verbatim_set',
  1713. );
  1714. $properties['family'] = array(
  1715. 'type' => 'text',
  1716. 'label' => t('The family of the name.'),
  1717. 'setter callback' => 'entity_property_verbatim_set',
  1718. );
  1719. $properties['generational'] = array(
  1720. 'type' => 'text',
  1721. 'label' => t('The generational of the name.'),
  1722. 'setter callback' => 'entity_property_verbatim_set',
  1723. );
  1724. $properties['credentials'] = array(
  1725. 'type' => 'text',
  1726. 'label' => t('The credentials of the name.'),
  1727. 'setter callback' => 'entity_property_verbatim_set',
  1728. );
  1729. return $properties;
  1730. }
  1731. /**
  1732. * Implements hook_migrate_api().
  1733. */
  1734. function name_migrate_api() {
  1735. return array(
  1736. 'api' => 2,
  1737. 'field handlers' => array(
  1738. 'MigrateNameHandler',
  1739. ),
  1740. );
  1741. }
  1742. /**
  1743. * Implements hook_features_api().
  1744. */
  1745. function name_features_api() {
  1746. return array(
  1747. 'namefield' => array(
  1748. 'name' => t('Name formats'),
  1749. 'default_hook' => 'name_default_formats',
  1750. 'feature_source' => TRUE,
  1751. 'file' => drupal_get_path('module', 'name') . '/includes/name.features.inc',
  1752. ),
  1753. );
  1754. }
  1755. /**
  1756. * Implements hook_name_data_sources().
  1757. *
  1758. * @todo
  1759. * Consider using field data as a source.
  1760. */
  1761. function name_name_data_sources() {
  1762. return array(
  1763. 'title' => array(
  1764. 'name' => t('Title options'),
  1765. 'components' => array('title'),
  1766. 'autocomplete callback' => 'name_field_autocomplete_query',
  1767. 'autocomplete arguments' => array('title'),
  1768. 'list callback' => 'name_field_get_options',
  1769. ),
  1770. 'generational' => array(
  1771. 'name' => t('Generational options'),
  1772. 'components' => array('generational'),
  1773. 'autocomplete callback' => 'name_field_autocomplete_query',
  1774. 'autocomplete arguments' => array('generational'),
  1775. 'list callback' => 'name_field_get_options',
  1776. ),
  1777. );
  1778. }