profile2.module 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  1. <?php
  2. /**
  3. * @file
  4. * Support for configurable user profiles.
  5. */
  6. /**
  7. * Implements hook_entity_info().
  8. */
  9. function profile2_entity_info() {
  10. $return = array(
  11. 'profile2' => array(
  12. 'label' => t('Profile'),
  13. 'plural label' => t('Profiles'),
  14. 'description' => t('Profile2 user profiles.'),
  15. 'entity class' => 'Profile',
  16. 'controller class' => 'EntityAPIController',
  17. 'base table' => 'profile',
  18. 'fieldable' => TRUE,
  19. 'view modes' => array(
  20. 'account' => array(
  21. 'label' => t('User account'),
  22. 'custom settings' => FALSE,
  23. ),
  24. ),
  25. 'entity keys' => array(
  26. 'id' => 'pid',
  27. 'bundle' => 'type',
  28. 'label' => 'label',
  29. ),
  30. 'bundles' => array(),
  31. 'bundle keys' => array(
  32. 'bundle' => 'type',
  33. ),
  34. 'label callback' => 'entity_class_label',
  35. 'uri callback' => 'entity_class_uri',
  36. 'access callback' => 'profile2_access',
  37. 'module' => 'profile2',
  38. 'metadata controller class' => 'Profile2MetadataController'
  39. ),
  40. );
  41. // Add bundle info but bypass entity_load() as we cannot use it here.
  42. $types = db_select('profile_type', 'p')
  43. ->fields('p')
  44. ->execute()
  45. ->fetchAllAssoc('type');
  46. foreach ($types as $type => $info) {
  47. $return['profile2']['bundles'][$type] = array(
  48. 'label' => $info->label,
  49. 'admin' => array(
  50. 'path' => 'admin/structure/profiles/manage/%profile2_type',
  51. 'real path' => 'admin/structure/profiles/manage/' . $type,
  52. 'bundle argument' => 4,
  53. 'access arguments' => array('administer profiles'),
  54. ),
  55. );
  56. }
  57. // Support entity cache module.
  58. if (module_exists('entitycache')) {
  59. $return['profile2']['field cache'] = FALSE;
  60. $return['profile2']['entity cache'] = TRUE;
  61. }
  62. $return['profile2_type'] = array(
  63. 'label' => t('Profile type'),
  64. 'plural label' => t('Profile types'),
  65. 'description' => t('Profiles types of Profile2 user profiles.'),
  66. 'entity class' => 'ProfileType',
  67. 'controller class' => 'EntityAPIControllerExportable',
  68. 'base table' => 'profile_type',
  69. 'fieldable' => FALSE,
  70. 'bundle of' => 'profile2',
  71. 'exportable' => TRUE,
  72. 'entity keys' => array(
  73. 'id' => 'id',
  74. 'name' => 'type',
  75. 'label' => 'label',
  76. ),
  77. 'access callback' => 'profile2_type_access',
  78. 'module' => 'profile2',
  79. // Enable the entity API's admin UI.
  80. 'admin ui' => array(
  81. 'path' => 'admin/structure/profiles',
  82. 'file' => 'profile2.admin.inc',
  83. 'controller class' => 'Profile2TypeUIController',
  84. ),
  85. );
  86. return $return;
  87. }
  88. /**
  89. * Menu argument loader; Load a profile type by string.
  90. *
  91. * @param $type
  92. * The machine-readable name of a profile type to load.
  93. * @return
  94. * A profile type array or FALSE if $type does not exist.
  95. */
  96. function profile2_type_load($type) {
  97. return profile2_get_types($type);
  98. }
  99. /**
  100. * Implements hook_permission().
  101. */
  102. function profile2_permission() {
  103. $permissions = array(
  104. 'administer profile types' => array(
  105. 'title' => t('Administer profile types'),
  106. 'description' => t('Create and delete fields on user profiles, and set their permissions.'),
  107. ),
  108. 'administer profiles' => array(
  109. 'title' => t('Administer profiles'),
  110. 'description' => t('Edit and view all user profiles.'),
  111. ),
  112. );
  113. // Generate per profile type permissions.
  114. foreach (profile2_get_types() as $type) {
  115. $type_name = check_plain($type->type);
  116. $permissions += array(
  117. "edit own $type_name profile" => array(
  118. 'title' => t('%type_name: Edit own profile', array('%type_name' => $type->getTranslation('label'))),
  119. ),
  120. "edit any $type_name profile" => array(
  121. 'title' => t('%type_name: Edit any profile', array('%type_name' => $type->getTranslation('label'))),
  122. ),
  123. "view own $type_name profile" => array(
  124. 'title' => t('%type_name: View own profile', array('%type_name' => $type->getTranslation('label'))),
  125. ),
  126. "view any $type_name profile" => array(
  127. 'title' => t('%type_name: View any profile', array('%type_name' => $type->getTranslation('label'))),
  128. ),
  129. );
  130. }
  131. return $permissions;
  132. }
  133. /**
  134. * Gets an array of all profile types, keyed by the type name.
  135. *
  136. * @param $type_name
  137. * If set, the type with the given name is returned.
  138. * @return ProfileType[]
  139. * Depending whether $type isset, an array of profile types or a single one.
  140. */
  141. function profile2_get_types($type_name = NULL) {
  142. $types = entity_load_multiple_by_name('profile2_type', isset($type_name) ? array($type_name) : FALSE);
  143. return isset($type_name) ? reset($types) : $types;
  144. }
  145. /**
  146. * Fetch a profile object.
  147. *
  148. * @param $pid
  149. * Integer specifying the profile id.
  150. * @param $reset
  151. * A boolean indicating that the internal cache should be reset.
  152. * @return
  153. * A fully-loaded $profile object or FALSE if it cannot be loaded.
  154. *
  155. * @see profile2_load_multiple()
  156. */
  157. function profile2_load($pid, $reset = FALSE) {
  158. $profiles = profile2_load_multiple(array($pid), array(), $reset);
  159. return reset($profiles);
  160. }
  161. /**
  162. * Load multiple profiles based on certain conditions.
  163. *
  164. * @param $pids
  165. * An array of profile IDs.
  166. * @param $conditions
  167. * An array of conditions to match against the {profile} table.
  168. * @param $reset
  169. * A boolean indicating that the internal cache should be reset.
  170. * @return
  171. * An array of profile objects, indexed by pid.
  172. *
  173. * @see entity_load()
  174. * @see profile2_load()
  175. * @see profile2_load_by_user()
  176. */
  177. function profile2_load_multiple($pids = array(), $conditions = array(), $reset = FALSE) {
  178. return entity_load('profile2', $pids, $conditions, $reset);
  179. }
  180. /**
  181. * Fetch profiles by account.
  182. *
  183. * @param $account
  184. * The user account to load profiles for, or its uid.
  185. * @param $type_name
  186. * To load a single profile, pass the type name of the profile to load.
  187. * @return
  188. * Either a single profile or an array of profiles keyed by profile type.
  189. *
  190. * @see profile2_load_multiple()
  191. * @see profile2_profile2_delete()
  192. * @see Profile::save()
  193. */
  194. function profile2_load_by_user($account, $type_name = NULL) {
  195. // Use a separate query to determine all profile ids per user and cache them.
  196. // That way we can look up profiles by id and benefit from the static cache
  197. // of the entity loader.
  198. $cache = &drupal_static(__FUNCTION__, array());
  199. $uid = is_object($account) ? $account->uid : $account;
  200. if (!isset($cache[$uid])) {
  201. if (empty($type_name)) {
  202. $profiles = profile2_load_multiple(FALSE, array('uid' => $uid));
  203. // Cache ids for further lookups.
  204. $cache[$uid] = array();
  205. foreach ($profiles as $pid => $profile) {
  206. $cache[$uid][$profile->type] = $pid;
  207. }
  208. return $profiles ? array_combine(array_keys($cache[$uid]), $profiles) : array();
  209. }
  210. $cache[$uid] = db_select('profile', 'p')
  211. ->fields('p', array('type', 'pid'))
  212. ->condition('uid', $uid)
  213. ->execute()
  214. ->fetchAllKeyed();
  215. }
  216. if (isset($type_name)) {
  217. return isset($cache[$uid][$type_name]) ? profile2_load($cache[$uid][$type_name]) : FALSE;
  218. }
  219. // Return an array containing profiles keyed by profile type.
  220. return $cache[$uid] ? array_combine(array_keys($cache[$uid]), profile2_load_multiple($cache[$uid])) : $cache[$uid];
  221. }
  222. /**
  223. * Implements hook_profile2_delete().
  224. */
  225. function profile2_profile2_delete($profile) {
  226. // Clear the static cache from profile2_load_by_user().
  227. $cache = &drupal_static('profile2_load_by_user', array());
  228. unset($cache[$profile->uid][$profile->type]);
  229. }
  230. /**
  231. * Deletes a profile.
  232. */
  233. function profile2_delete(Profile $profile) {
  234. $profile->delete();
  235. }
  236. /**
  237. * Delete multiple profiles.
  238. *
  239. * @param $pids
  240. * An array of profile IDs.
  241. */
  242. function profile2_delete_multiple(array $pids) {
  243. entity_get_controller('profile2')->delete($pids);
  244. }
  245. /**
  246. * Implements hook_user_delete().
  247. */
  248. function profile2_user_delete($account) {
  249. foreach (profile2_load_by_user($account) as $profile) {
  250. profile2_delete($profile);
  251. }
  252. }
  253. /**
  254. * Create a new profile object.
  255. */
  256. function profile2_create(array $values) {
  257. return new Profile($values);
  258. }
  259. /**
  260. * Deprecated. Use profile2_create().
  261. */
  262. function profile_create(array $values) {
  263. return new Profile($values);
  264. }
  265. /**
  266. * Saves a profile to the database.
  267. *
  268. * @param $profile
  269. * The profile object.
  270. */
  271. function profile2_save(Profile $profile) {
  272. return $profile->save();
  273. }
  274. /**
  275. * Saves a profile type to the db.
  276. */
  277. function profile2_type_save(ProfileType $type) {
  278. $type->save();
  279. }
  280. /**
  281. * Deletes a profile type from.
  282. */
  283. function profile2_type_delete(ProfileType $type) {
  284. $type->delete();
  285. }
  286. /**
  287. * Implements hook_profile2_type_delete()
  288. */
  289. function profile2_profile2_type_delete(ProfileType $type) {
  290. // Delete all profiles of this type but only if this is not a revert.
  291. if (!$type->hasStatus(ENTITY_IN_CODE)) {
  292. $pids = array_keys(profile2_load_multiple(FALSE, array('type' => $type->type)));
  293. if ($pids) {
  294. profile2_delete_multiple($pids);
  295. }
  296. }
  297. }
  298. /**
  299. * Implements hook_user_view().
  300. */
  301. function profile2_user_view($account, $view_mode, $langcode) {
  302. foreach (profile2_get_types() as $type => $profile_type) {
  303. if ($profile_type->userView && $profile = profile2_load_by_user($account, $type)) {
  304. if (profile2_access('view', $profile)) {
  305. $account->content['profile_' . $type] = array(
  306. '#type' => 'user_profile_category',
  307. '#title' => $profile->label,
  308. '#prefix' => '<a id="profile-' . $profile->type . '"></a>',
  309. );
  310. $account->content['profile_' . $type]['view'] = $profile->view('account');
  311. }
  312. }
  313. }
  314. }
  315. /**
  316. * Implements hook_form_FORM_ID_alter() for the user edit form.
  317. *
  318. * @see profile2_form_validate_handler
  319. * @see profile2_form_submit_handler
  320. */
  321. function profile2_form_user_profile_form_alter(&$form, &$form_state) {
  322. if (($type = profile2_get_types($form['#user_category'])) && $type->userCategory) {
  323. if (empty($form_state['profiles'])) {
  324. $profile = profile2_load_by_user($form['#user'], $form['#user_category']);
  325. if (empty($profile)) {
  326. $profile = profile2_create(array('type' => $form['#user_category'], 'uid' => $form['#user']->uid));
  327. }
  328. $form_state['profiles'][$profile->type] = $profile;
  329. }
  330. profile2_attach_form($form, $form_state);
  331. }
  332. }
  333. /**
  334. * Implements hook_form_FORM_ID_alter() for the registration form.
  335. */
  336. function profile2_form_user_register_form_alter(&$form, &$form_state) {
  337. foreach (profile2_get_types() as $type_name => $profile_type) {
  338. if (!empty($profile_type->data['registration'])) {
  339. if (empty($form_state['profiles'][$type_name])) {
  340. $form_state['profiles'][$type_name] = profile2_create(array('type' => $type_name));
  341. }
  342. profile2_attach_form($form, $form_state);
  343. // Wrap each profile form in a fieldset.
  344. $form['profile_' . $type_name] += array(
  345. '#type' => 'fieldset',
  346. '#title' => check_plain($profile_type->getTranslation('label')),
  347. );
  348. }
  349. }
  350. }
  351. /**
  352. * Attaches the profile forms of the profiles set in
  353. * $form_state['profiles'].
  354. *
  355. * Modules may alter the profile2 entity form regardless to which form it is
  356. * attached by making use of hook_form_profile2_form_alter().
  357. *
  358. * @param $form
  359. * The form to which to attach the profile2 form. For each profile the form
  360. * is added to @code $form['profile_' . $profile->type] @endcode. This helper
  361. * also adds in a validation and a submit handler caring for the attached
  362. * profile forms.
  363. *
  364. * @see hook_form_profile2_form_alter()
  365. * @see profile2_form_validate_handler()
  366. * @see profile2_form_submit_handler()
  367. */
  368. function profile2_attach_form(&$form, &$form_state) {
  369. foreach ($form_state['profiles'] as $type => $profile) {
  370. $form['profile_' . $profile->type]['#tree'] = TRUE;
  371. $form['profile_' . $profile->type]['#parents'] = array('profile_' . $profile->type);
  372. field_attach_form('profile2', $profile, $form['profile_' . $profile->type], $form_state);
  373. if (count(field_info_instances('profile2', $profile->type)) == 0) {
  374. $form['profile_' . $profile->type]['message'] = array(
  375. '#access' => user_access('administer profile types'),
  376. '#markup' => t('No fields have been associated with this profile type. Go to the <a href="!url">Profile types</a> page to add some fields.', array('!url' => url('admin/structure/profiles'))),
  377. );
  378. }
  379. // Provide a central place for modules to alter the profile forms, but
  380. // skip that in case the caller cares about invoking the hooks.
  381. // @see profile2_form().
  382. if (!isset($form_state['profile2_skip_hook'])) {
  383. $hooks[] = 'form_profile2_edit_' . $type . '_form';
  384. $hooks[] = 'form_profile2_form';
  385. drupal_alter($hooks, $form, $form_state);
  386. }
  387. }
  388. $form['#validate'][] = 'profile2_form_validate_handler';
  389. $form['#submit'][] = 'profile2_form_submit_handler';
  390. }
  391. /**
  392. * Validation handler for the profile form.
  393. *
  394. * @see profile2_attach_form()
  395. */
  396. function profile2_form_validate_handler(&$form, &$form_state) {
  397. foreach ($form_state['profiles'] as $type => $profile) {
  398. if (isset($form_state['values']['profile_' . $profile->type])) {
  399. // @see entity_form_field_validate()
  400. $pseudo_entity = (object) $form_state['values']['profile_' . $profile->type];
  401. $pseudo_entity->type = $type;
  402. field_attach_form_validate('profile2', $pseudo_entity, $form['profile_' . $profile->type], $form_state);
  403. }
  404. }
  405. }
  406. /**
  407. * Submit handler that builds and saves all profiles in the form.
  408. *
  409. * @see profile2_attach_form()
  410. */
  411. function profile2_form_submit_handler(&$form, &$form_state) {
  412. profile2_form_submit_build_profile($form, $form_state);
  413. // This is needed as some submit callbacks like user_register_submit() rely on
  414. // clean form values.
  415. profile2_form_submit_cleanup($form, $form_state);
  416. foreach ($form_state['profiles'] as $type => $profile) {
  417. // During registration set the uid field of the newly created user.
  418. if (empty($profile->uid) && isset($form_state['user']->uid)) {
  419. $profile->uid = $form_state['user']->uid;
  420. }
  421. profile2_save($profile);
  422. }
  423. }
  424. /**
  425. * Submit builder. Extracts the form values and updates the profile entities.
  426. *
  427. * @see profile2_attach_form()
  428. */
  429. function profile2_form_submit_build_profile(&$form, &$form_state) {
  430. foreach ($form_state['profiles'] as $type => $profile) {
  431. // @see entity_form_submit_build_entity()
  432. if (isset($form['profile_' . $type]['#entity_builders'])) {
  433. foreach ($form['profile_' . $type]['#entity_builders'] as $function) {
  434. $function('profile2', $profile, $form['profile_' . $type], $form_state);
  435. }
  436. }
  437. field_attach_submit('profile2', $profile, $form['profile_' . $type], $form_state);
  438. }
  439. }
  440. /**
  441. * Cleans up the form values as the user modules relies on clean values.
  442. *
  443. * @see profile2_attach_form()
  444. */
  445. function profile2_form_submit_cleanup(&$form, &$form_state) {
  446. foreach ($form_state['profiles'] as $type => $profile) {
  447. unset($form_state['values']['profile_' . $type]);
  448. }
  449. }
  450. /**
  451. * Implements hook_user_categories().
  452. */
  453. function profile2_user_categories() {
  454. $data = array();
  455. foreach (profile2_get_types() as $type => $info) {
  456. if ($info->userCategory) {
  457. $data[] = array(
  458. 'name' => $type,
  459. 'title' => $info->getTranslation('label'),
  460. // Add an offset so a weight of 0 appears right of the account category.
  461. 'weight' => $info->weight + 3,
  462. 'access callback' => 'profile2_category_access',
  463. 'access arguments' => array(1, $type)
  464. );
  465. }
  466. }
  467. return $data;
  468. }
  469. /**
  470. * Menu item access callback - check if a user has access to a profile category.
  471. */
  472. function profile2_category_access($account, $type_name) {
  473. // As there might be no profile yet, create a new object for being able to run
  474. // a proper access check.
  475. $profile = profile2_create(array('type' => $type_name, 'uid' => $account->uid));
  476. return ($account->uid > 0 && $profile->type()->userCategory && profile2_access('edit', $profile));
  477. }
  478. /**
  479. * Determines whether the given user has access to a profile.
  480. *
  481. * @param $op
  482. * The operation being performed. One of 'view', 'update', 'create', 'delete'
  483. * or just 'edit' (being the same as 'create' or 'update').
  484. * @param $profile
  485. * (optional) A profile to check access for. If nothing is given, access for
  486. * all profiles is determined.
  487. * @param $account
  488. * The user to check for. Leave it to NULL to check for the global user.
  489. * @return boolean
  490. * Whether access is allowed or not.
  491. *
  492. * @see hook_profile2_access()
  493. * @see profile2_profile2_access()
  494. */
  495. function profile2_access($op, $profile = NULL, $account = NULL) {
  496. if (user_access('administer profiles', $account)) {
  497. return TRUE;
  498. }
  499. if ($op == 'create' || $op == 'update') {
  500. $op = 'edit';
  501. }
  502. // Allow modules to grant / deny access.
  503. $access = module_invoke_all('profile2_access', $op, $profile, $account);
  504. // Only grant access if at least one module granted access and no one denied
  505. // access.
  506. if (in_array(FALSE, $access, TRUE)) {
  507. return FALSE;
  508. }
  509. elseif (in_array(TRUE, $access, TRUE)) {
  510. return TRUE;
  511. }
  512. return FALSE;
  513. }
  514. /**
  515. * Implements hook_profile2_access().
  516. */
  517. function profile2_profile2_access($op, $profile = NULL, $account = NULL) {
  518. // Don't grant access for users to delete their profile.
  519. if (isset($profile) && ($type_name = $profile->type) && $op != 'delete') {
  520. if (user_access("$op any $type_name profile", $account)) {
  521. return TRUE;
  522. }
  523. $account = isset($account) ? $account : $GLOBALS['user'];
  524. if (isset($profile->uid) && $profile->uid == $account->uid && user_access("$op own $type_name profile", $account)) {
  525. return TRUE;
  526. }
  527. }
  528. // Do not explicitly deny access so others may still grant access.
  529. }
  530. /**
  531. * Access callback for the entity API.
  532. */
  533. function profile2_type_access($op, $type = NULL, $account = NULL) {
  534. return user_access('administer profile types', $account);
  535. }
  536. /**
  537. * Implements hook_theme().
  538. */
  539. function profile2_theme() {
  540. return array(
  541. 'profile2' => array(
  542. 'render element' => 'elements',
  543. 'template' => 'profile2',
  544. ),
  545. );
  546. }
  547. /**
  548. * The class used for profile entities.
  549. */
  550. class Profile extends Entity {
  551. /**
  552. * The profile id.
  553. *
  554. * @var integer
  555. */
  556. public $pid;
  557. /**
  558. * The name of the profile type.
  559. *
  560. * @var string
  561. */
  562. public $type;
  563. /**
  564. * The profile label.
  565. *
  566. * @var string
  567. */
  568. public $label;
  569. /**
  570. * The user id of the profile owner.
  571. *
  572. * @var integer
  573. */
  574. public $uid;
  575. /**
  576. * The Unix timestamp when the profile was created.
  577. *
  578. * @var integer
  579. */
  580. public $created;
  581. /**
  582. * The Unix timestamp when the profile was most recently saved.
  583. *
  584. * @var integer
  585. */
  586. public $changed;
  587. public function __construct($values = array()) {
  588. if (isset($values['user'])) {
  589. $this->setUser($values['user']);
  590. unset($values['user']);
  591. }
  592. if (isset($values['type']) && is_object($values['type'])) {
  593. $values['type'] = $values['type']->type;
  594. }
  595. if (!isset($values['label']) && isset($values['type']) && $type = profile2_get_types($values['type'])) {
  596. // Initialize the label with the type label, so newly created profiles
  597. // have that as interim label.
  598. $values['label'] = $type->label;
  599. }
  600. parent::__construct($values, 'profile2');
  601. }
  602. /**
  603. * Returns the user owning this profile.
  604. */
  605. public function user() {
  606. return user_load($this->uid);
  607. }
  608. /**
  609. * Sets a new user owning this profile.
  610. *
  611. * @param $account
  612. * The user account object or the user account id (uid).
  613. */
  614. public function setUser($account) {
  615. $this->uid = is_object($account) ? $account->uid : $account;
  616. }
  617. /**
  618. * Gets the associated profile type object.
  619. *
  620. * @return ProfileType
  621. */
  622. public function type() {
  623. return profile2_get_types($this->type);
  624. }
  625. /**
  626. * Returns the full url() for the profile.
  627. */
  628. public function url() {
  629. $uri = $this->uri();
  630. return url($uri['path'], $uri);
  631. }
  632. /**
  633. * Returns the drupal path to this profile.
  634. */
  635. public function path() {
  636. $uri = $this->uri();
  637. return $uri['path'];
  638. }
  639. public function defaultUri() {
  640. return array(
  641. 'path' => 'user/' . $this->uid,
  642. 'options' => array('fragment' => 'profile-' . $this->type),
  643. );
  644. }
  645. public function defaultLabel() {
  646. if (module_exists('profile2_i18n')) {
  647. // Run the label through i18n_string() using the profile2_type label
  648. // context, so the default label (= the type's label) gets translated.
  649. return entity_i18n_string('profile2:profile2_type:' . $this->type . ':label', $this->label);
  650. }
  651. return $this->label;
  652. }
  653. public function buildContent($view_mode = 'full', $langcode = NULL) {
  654. $content = array();
  655. // Assume newly create objects are still empty.
  656. if (!empty($this->is_new)) {
  657. $content['empty']['#markup'] = '<em class="profile2-no-data">' . t('There is no profile data yet.') . '</em>';
  658. }
  659. return entity_get_controller($this->entityType)->buildContent($this, $view_mode, $langcode, $content);
  660. }
  661. public function save() {
  662. // Care about setting created and changed values. But do not automatically
  663. // set a created values for already existing profiles.
  664. if (empty($this->created) && (!empty($this->is_new) || !$this->pid)) {
  665. $this->created = REQUEST_TIME;
  666. }
  667. $this->changed = REQUEST_TIME;
  668. parent::save();
  669. // Update the static cache from profile2_load_by_user().
  670. $cache = &drupal_static('profile2_load_by_user', array());
  671. if (isset($cache[$this->uid])) {
  672. $cache[$this->uid][$this->type] = $this->pid;
  673. }
  674. }
  675. }
  676. /**
  677. * Use a separate class for profile types so we can specify some defaults
  678. * modules may alter.
  679. */
  680. class ProfileType extends Entity {
  681. /**
  682. * Whether the profile type appears in the user categories.
  683. */
  684. public $userCategory = TRUE;
  685. /**
  686. * Whether the profile is displayed on the user account page.
  687. */
  688. public $userView = TRUE;
  689. public $type;
  690. public $label;
  691. public $weight = 0;
  692. public function __construct($values = array()) {
  693. parent::__construct($values, 'profile2_type');
  694. }
  695. /**
  696. * Returns whether the profile type is locked, thus may not be deleted or renamed.
  697. *
  698. * Profile types provided in code are automatically treated as locked, as well
  699. * as any fixed profile type.
  700. */
  701. public function isLocked() {
  702. return isset($this->status) && empty($this->is_new) && (($this->status & ENTITY_IN_CODE) || ($this->status & ENTITY_FIXED));
  703. }
  704. /**
  705. * Overridden, to introduce the method for old entity API versions (BC).
  706. *
  707. * @todo Remove once we bump the required entity API version.
  708. */
  709. public function getTranslation($property, $langcode = NULL) {
  710. if (module_exists('profile2_i18n')) {
  711. return parent::getTranslation($property, $langcode);
  712. }
  713. return $this->$property;
  714. }
  715. /**
  716. * Overrides Entity::save().
  717. */
  718. public function save() {
  719. parent::save();
  720. // Clear field info caches such that any changes to extra fields get
  721. // reflected.
  722. field_info_cache_clear();
  723. }
  724. }
  725. /**
  726. * View a profile.
  727. *
  728. * @see Profile::view()
  729. */
  730. function profile2_view($profile, $view_mode = 'full', $langcode = NULL, $page = NULL) {
  731. return $profile->view($view_mode, $langcode, $page);
  732. }
  733. /**
  734. * Implements hook_form_FORMID_alter().
  735. *
  736. * Adds a checkbox for controlling field view access to fields added to
  737. * profiles.
  738. */
  739. function profile2_form_field_ui_field_edit_form_alter(&$form, &$form_state) {
  740. if ($form['instance']['entity_type']['#value'] == 'profile2') {
  741. $form['field']['settings']['profile2_private'] = array(
  742. '#type' => 'checkbox',
  743. '#title' => t('Make the content of this field private.'),
  744. '#default_value' => !empty($form['#field']['settings']['profile2_private']),
  745. '#description' => t('If checked, the content of this field is only shown to the profile owner and administrators.'),
  746. );
  747. }
  748. else {
  749. // Add the value to the form so it isn't lost.
  750. $form['field']['settings']['profile2_private'] = array(
  751. '#type' => 'value',
  752. '#value' => !empty($form['#field']['settings']['profile2_private']),
  753. );
  754. }
  755. }
  756. /**
  757. * Implements hook_field_access().
  758. */
  759. function profile2_field_access($op, $field, $entity_type, $profile = NULL, $account = NULL) {
  760. if ($entity_type == 'profile2' && $op == 'view' && !empty($field['settings']['profile2_private']) && !user_access('administer profiles', $account)) {
  761. // For profiles, deny general view access for private fields.
  762. if (!isset($profile)) {
  763. return FALSE;
  764. }
  765. // Also deny view access, if someone else views a private field.
  766. $account = isset($account) ? $account : $GLOBALS['user'];
  767. if ($account->uid != $profile->uid) {
  768. return FALSE;
  769. }
  770. }
  771. }
  772. /**
  773. * Implements hook_field_extra_fields().
  774. *
  775. * We need to add pseudo fields for profile types to allow for weight settings
  776. * when viewing a user or filling in the profile types while registrating.
  777. */
  778. function profile2_field_extra_fields() {
  779. $extra = array();
  780. foreach (profile2_get_types() as $type_name => $type) {
  781. // Appears on: admin/config/people/accounts/display
  782. if (!empty($type->userView)) {
  783. $extra['user']['user']['display']['profile_' . $type_name] = array(
  784. 'label' => t('Profile: @profile', array('@profile' => $type->label)),
  785. 'weight' => $type->weight,
  786. );
  787. }
  788. // Appears on: admin/config/people/accounts/fields
  789. if (!empty($type->data['registration'])) {
  790. $extra['user']['user']['form']['profile_' . $type_name] = array(
  791. 'label' => t('Profile: @profile', array('@profile' => $type->label)),
  792. 'description' => t('Appears during registration only.'),
  793. 'weight' => $type->weight,
  794. );
  795. }
  796. }
  797. return $extra;
  798. }
  799. /**
  800. * Entity metadata callback to load profiles for the given user account.
  801. */
  802. function profile2_user_get_properties($account, array $options, $name) {
  803. // Remove the leading 'profile_' from the property name to get the type name.
  804. $profile = profile2_load_by_user($account, substr($name, 8));
  805. return $profile ? $profile : NULL;
  806. }