array(
'label' => t('Profile'),
'plural label' => t('Profiles'),
'description' => t('Profile2 user profiles.'),
'entity class' => 'Profile',
'controller class' => 'EntityAPIController',
'base table' => 'profile',
'fieldable' => TRUE,
'view modes' => array(
'account' => array(
'label' => t('User account'),
'custom settings' => FALSE,
),
),
'entity keys' => array(
'id' => 'pid',
'bundle' => 'type',
'label' => 'label',
),
'bundles' => array(),
'bundle keys' => array(
'bundle' => 'type',
),
'label callback' => 'entity_class_label',
'uri callback' => 'entity_class_uri',
'access callback' => 'profile2_access',
'module' => 'profile2',
'metadata controller class' => 'Profile2MetadataController'
),
);
// Add bundle info but bypass entity_load() as we cannot use it here.
$types = db_select('profile_type', 'p')
->fields('p')
->execute()
->fetchAllAssoc('type');
foreach ($types as $type => $info) {
$return['profile2']['bundles'][$type] = array(
'label' => $info->label,
'admin' => array(
'path' => 'admin/structure/profiles/manage/%profile2_type',
'real path' => 'admin/structure/profiles/manage/' . $type,
'bundle argument' => 4,
'access arguments' => array('administer profiles'),
),
);
}
// Support entity cache module.
if (module_exists('entitycache')) {
$return['profile2']['field cache'] = FALSE;
$return['profile2']['entity cache'] = TRUE;
}
$return['profile2_type'] = array(
'label' => t('Profile type'),
'plural label' => t('Profile types'),
'description' => t('Profiles types of Profile2 user profiles.'),
'entity class' => 'ProfileType',
'controller class' => 'EntityAPIControllerExportable',
'base table' => 'profile_type',
'fieldable' => FALSE,
'bundle of' => 'profile2',
'exportable' => TRUE,
'entity keys' => array(
'id' => 'id',
'name' => 'type',
'label' => 'label',
),
'access callback' => 'profile2_type_access',
'module' => 'profile2',
// Enable the entity API's admin UI.
'admin ui' => array(
'path' => 'admin/structure/profiles',
'file' => 'profile2.admin.inc',
'controller class' => 'Profile2TypeUIController',
),
);
return $return;
}
/**
* Menu argument loader; Load a profile type by string.
*
* @param $type
* The machine-readable name of a profile type to load.
* @return
* A profile type array or FALSE if $type does not exist.
*/
function profile2_type_load($type) {
return profile2_get_types($type);
}
/**
* Implements hook_menu().
*/
function profile2_menu() {
$items = array();
// Define page which provides form to generate profiles using
// Devel generate module.
if (module_exists('devel_generate')) {
$items['admin/config/development/generate/profile2'] = array(
'title' => 'Generate profiles',
'description' => 'Generate a given number of profiles for users. Optionally override current user profiles.',
'access arguments' => array('administer profiles'),
'page callback' => 'drupal_get_form',
'page arguments' => array('profile2_generate_form'),
'file' => 'profile2.devel.inc',
);
}
$items['user/%profile2_by_uid/%/delete'] = array(
'title' => 'Delete',
'description' => 'Delete Profile of User.',
'type' => MENU_NORMAL_ITEM,
'page callback' => 'drupal_get_form',
'page arguments' => array('profile2_delete_confirm_form', 1),
'load arguments' => array(2),
'access callback' => 'profile2_access',
'access arguments' => array('delete', 1),
'file' => 'profile2.delete.inc',
);
return $items;
}
/**
* Implements hook_permission().
*/
function profile2_permission() {
$permissions = array(
'administer profile types' => array(
'title' => t('Administer profile types'),
'description' => t('Create and delete fields on user profiles, and set their permissions.'),
),
'administer profiles' => array(
'title' => t('Administer profiles'),
'description' => t('Edit and view all user profiles.'),
),
);
// Generate per profile type permissions.
foreach (profile2_get_types() as $type) {
$type_name = check_plain($type->type);
$permissions += array(
"edit own $type_name profile" => array(
'title' => t('%type_name: Edit own profile', array('%type_name' => $type->getTranslation('label'))),
),
"edit any $type_name profile" => array(
'title' => t('%type_name: Edit any profile', array('%type_name' => $type->getTranslation('label'))),
),
"view own $type_name profile" => array(
'title' => t('%type_name: View own profile', array('%type_name' => $type->getTranslation('label'))),
),
"view any $type_name profile" => array(
'title' => t('%type_name: View any profile', array('%type_name' => $type->getTranslation('label'))),
),
"delete own $type_name profile" => array(
'title' => t('%type_name: Delete own profile', array('%type_name' => $type->getTranslation('label'))),
),
);
}
return $permissions;
}
/**
* Gets an array of all profile types, keyed by the type name.
*
* @param $type_name
* If set, the type with the given name is returned.
* @return ProfileType[]
* Depending whether $type isset, an array of profile types or a single one.
*/
function profile2_get_types($type_name = NULL) {
$types = entity_load_multiple_by_name('profile2_type', isset($type_name) ? array($type_name) : FALSE);
return isset($type_name) ? reset($types) : $types;
}
/**
* Fetch a profile object.
*
* @param $pid
* Integer specifying the profile id.
* @param $reset
* A boolean indicating that the internal cache should be reset.
* @return
* A fully-loaded $profile object or FALSE if it cannot be loaded.
*
* @see profile2_load_multiple()
*/
function profile2_load($pid, $reset = FALSE) {
$profiles = profile2_load_multiple(array($pid), array(), $reset);
return reset($profiles);
}
/**
* Load multiple profiles based on certain conditions.
*
* @param $pids
* An array of profile IDs.
* @param $conditions
* An array of conditions to match against the {profile} table.
* @param $reset
* A boolean indicating that the internal cache should be reset.
* @return
* An array of profile objects, indexed by pid.
*
* @see entity_load()
* @see profile2_load()
* @see profile2_load_by_user()
*/
function profile2_load_multiple($pids = array(), $conditions = array(), $reset = FALSE) {
return entity_load('profile2', $pids, $conditions, $reset);
}
/**
* Fetch profiles by account.
*
* @param $account
* The user account to load profiles for, or its uid.
* @param $type_name
* To load a single profile, pass the type name of the profile to load.
* @return
* Either a single profile or an array of profiles keyed by profile type.
*
* @see profile2_load_multiple()
* @see profile2_profile2_delete()
* @see Profile::save()
*/
function profile2_load_by_user($account, $type_name = NULL) {
// Use a separate query to determine all profile ids per user and cache them.
// That way we can look up profiles by id and benefit from the static cache
// of the entity loader.
$cache = &drupal_static(__FUNCTION__, array());
$uid = is_object($account) ? $account->uid : $account;
if (!isset($cache[$uid])) {
$cache[$uid] = db_select('profile', 'p')
->fields('p', array('type', 'pid'))
->condition('uid', $uid)
->execute()
->fetchAllKeyed();
}
if (isset($type_name)) {
return isset($cache[$uid][$type_name]) ? profile2_load($cache[$uid][$type_name]) : FALSE;
}
// Return an array containing profiles keyed by profile type.
return $cache[$uid] ? array_combine(array_keys($cache[$uid]), profile2_load_multiple($cache[$uid])) : $cache[$uid];
}
/**
* Implements hook_profile2_delete().
*/
function profile2_profile2_delete($profile) {
// Clear the static cache from profile2_load_by_user().
$cache = &drupal_static('profile2_load_by_user', array());
unset($cache[$profile->uid][$profile->type]);
}
/**
* Deletes a profile.
*/
function profile2_delete(Profile $profile) {
$profile->delete();
}
/**
* Delete multiple profiles.
*
* @param $pids
* An array of profile IDs.
*/
function profile2_delete_multiple(array $pids) {
entity_get_controller('profile2')->delete($pids);
}
/**
* Implements hook_user_delete().
*/
function profile2_user_delete($account) {
foreach (profile2_load_by_user($account) as $profile) {
profile2_delete($profile);
}
}
/**
* Create a new profile object.
*/
function profile2_create(array $values) {
return new Profile($values);
}
/**
* Deprecated. Use profile2_create().
*/
function profile_create(array $values) {
return new Profile($values);
}
/**
* Saves a profile to the database.
*
* @param $profile
* The profile object.
*/
function profile2_save(Profile $profile) {
return $profile->save();
}
/**
* Saves a profile type to the db.
*/
function profile2_type_save(ProfileType $type) {
$type->save();
}
/**
* Deletes a profile type from.
*/
function profile2_type_delete(ProfileType $type) {
$type->delete();
}
/**
* Implements hook_profile2_type_insert().
*/
function profile2_profile2_type_insert(ProfileType $type) {
// Do not directly issue menu rebuilds here to avoid potentially multiple
// rebuilds. Instead, let menu_get_item() issue the rebuild on the next page.
variable_set('menu_rebuild_needed', TRUE);
}
/**
* Implements hook_profile2_type_update().
*/
function profile2_profile2_type_update(ProfileType $type) {
// @see profile2_profile2_type_insert()
variable_set('menu_rebuild_needed', TRUE);
}
/**
* Implements hook_profile2_type_delete()
*/
function profile2_profile2_type_delete(ProfileType $type) {
// Delete all profiles of this type but only if this is not a revert.
if (!$type->hasStatus(ENTITY_IN_CODE)) {
$pids = array_keys(profile2_load_multiple(FALSE, array('type' => $type->type)));
if ($pids) {
profile2_delete_multiple($pids);
}
// @see profile2_profile2_type_insert()
variable_set('menu_rebuild_needed', TRUE);
}
}
/**
* Implements hook_user_view().
*/
function profile2_user_view($account, $view_mode, $langcode) {
foreach (profile2_get_types() as $type => $profile_type) {
if ($profile_type->userView && $profile = profile2_load_by_user($account, $type)) {
if (profile2_access('view', $profile)) {
$account->content['profile_' . $type] = array(
'#type' => 'user_profile_category',
'#title' => $profile_type->getTranslation('label'),
'#prefix' => '',
);
$account->content['profile_' . $type]['view'] = $profile->view($view_mode);
}
}
}
}
/**
* Implements hook_form_FORM_ID_alter() for the user edit form.
*
* @see profile2_form_validate_handler
* @see profile2_form_submit_handler
*/
function profile2_form_user_profile_form_alter(&$form, &$form_state) {
global $user;
if (($type = profile2_get_types($form['#user_category'])) && $type->userCategory) {
if (empty($form_state['profiles'])) {
$profile = profile2_load_by_user($form['#user'], $form['#user_category']);
if (empty($profile)) {
$profile = profile2_create(array('type' => $form['#user_category'], 'uid' => $form['#user']->uid));
$profile->is_new = TRUE;
}
$form_state['profiles'][$profile->type] = $profile;
if (user_access('administer profiles') && $user->uid != $profile->uid) {
$str_button_value = t('Delete profile');
}
elseif (user_access("delete own $profile->type profile") && $user->uid === $profile->uid) {
$str_button_value = t('Delete this profile');
}
}
if (empty($profile->is_new) && !empty($str_button_value)) {
$form['actions']['delete'] = array(
'#type' => 'submit',
'#value' => $str_button_value,
'#weight' => 45,
'#limit_validation_errors' => array(),
'#submit' => array('profile2_form_submit_own_delete')
);
}
profile2_attach_form($form, $form_state);
}
}
/**
* Profile form submit handler for the delete button.
*/
function profile2_form_submit_own_delete($form, &$form_state) {
$profile = $form_state['profiles'][$form['#user_category']];
if (isset($profile) && is_object($profile)) {
$form_state['redirect'] = 'user/' . $profile->uid . '/' . $form['#user_category'] . '/delete';
}
}
/**
* Implements hook_form_FORM_ID_alter() for the registration form.
*/
function profile2_form_user_register_form_alter(&$form, &$form_state) {
foreach (profile2_get_types() as $type_name => $profile_type) {
if (!empty($profile_type->data['registration'])) {
if (empty($form_state['profiles'][$type_name])) {
$form_state['profiles'][$type_name] = profile2_create(array('type' => $type_name));
}
}
}
// If we have profiles to attach to the registration form - then do it.
if (!empty($form_state['profiles'])) {
profile2_attach_form($form, $form_state);
// Wrap each profile form in a fieldset.
foreach ($form_state['profiles'] as $type_name => $profile_type) {
$form['profile_' . $type_name] += array(
'#type' => 'fieldset',
'#title' => check_plain($profile_type->getTranslation('label')),
);
}
}
}
/**
* Attaches the profile forms of the profiles set in
* $form_state['profiles'].
*
* Modules may alter the profile2 entity form regardless to which form it is
* attached by making use of hook_form_profile2_form_alter().
*
* @param $form
* The form to which to attach the profile2 form. For each profile the form
* is added to @code $form['profile_' . $profile->type] @endcode. This helper
* also adds in a validation and a submit handler caring for the attached
* profile forms.
*
* @see hook_form_profile2_form_alter()
* @see profile2_form_validate_handler()
* @see profile2_form_submit_handler()
*/
function profile2_attach_form(&$form, &$form_state) {
foreach ($form_state['profiles'] as $type => $profile) {
$form['profile_' . $profile->type]['#tree'] = TRUE;
$form['profile_' . $profile->type]['#parents'] = array('profile_' . $profile->type);
field_attach_form('profile2', $profile, $form['profile_' . $profile->type], $form_state);
if (user_access('administer profile types')) {
if (count(field_info_instances('profile2', $profile->type)) == 0) {
$form['profile_' . $profile->type]['message'] = array(
'#markup' => t('No fields have been associated with this profile type. Go to the Profile types page to add some fields.', array('!url' => url('admin/structure/profiles'))),
);
}
}
// Make sure we don't have duplicate pre render callbacks.
$form['profile_' . $profile->type]['#pre_render'] = array_unique($form['profile_' . $profile->type]['#pre_render']);
// Provide a central place for modules to alter the profile forms, but
// skip that in case the caller cares about invoking the hooks.
// @see profile2_form().
if (!isset($form_state['profile2_skip_hook'])) {
$hooks[] = 'form_profile2_edit_' . $type . '_form';
$hooks[] = 'form_profile2_form';
drupal_alter($hooks, $form, $form_state);
}
}
$form['#validate'][] = 'profile2_form_validate_handler';
// Default name of user registry form callback.
$register_submit_callback = 'user_register_submit';
// LoginToBoggan module replaces default user_register_submit() callback
// with his own. So if this module enabled we need to track his callback
// instead one that comes from the User module.
if (module_exists('logintoboggan')) {
$register_submit_callback = 'logintoboggan_user_register_submit';
}
// Search for key of user register submit callback.
if (!empty($form['#submit']) && is_array($form['#submit'])) {
$submit_key = array_search($register_submit_callback, $form['#submit']);
}
// Add these hooks only when needed, and ensure they are not added twice.
if (isset($submit_key) && $submit_key !== FALSE && !in_array('profile2_form_before_user_register_submit_handler', $form['#submit'])) {
// Insert submit callback right before the user register submit callback.
// Needs for disabling email notification during user registration.
array_splice($form['#submit'], $submit_key, 0, array('profile2_form_before_user_register_submit_handler'));
// Add a submit callback right after the user register submit callback.
// This is needed for creation of a new user profile.
array_splice($form['#submit'], $submit_key + 2, 0, array('profile2_form_submit_handler'));
// Insert submit handler right after the creation of new user profile.
// This is needed for sending email which was blocked during registration.
array_splice($form['#submit'], $submit_key + 3, 0, array('profile2_form_after_user_register_submit_handler'));
}
else {
// Fallback if some contrib module removes user register submit callback
// from form submit functions.
$form['#submit'][] = 'profile2_form_submit_handler';
}
}
/**
* Validation handler for the profile form.
*
* @see profile2_attach_form()
*/
function profile2_form_validate_handler(&$form, &$form_state) {
foreach ($form_state['profiles'] as $type => $profile) {
if (isset($form_state['values']['profile_' . $profile->type])) {
// @see entity_form_field_validate()
$pseudo_entity = (object) $form_state['values']['profile_' . $profile->type];
$pseudo_entity->type = $type;
field_attach_form_validate('profile2', $pseudo_entity, $form['profile_' . $profile->type], $form_state);
}
}
}
/**
* User registration form submit handler
* that executes right before user_register_submit().
*
* In generally, this callback disables the notification emails
* during the execution of user_register_submit() callback.
* The reason for this - we want to support profile2 tokens
* in emails during registration, and there is no another
* proper way to do this. See https://drupal.org/node/1097684.
*
* @see profile2_form_after_user_register_submit_handler()
* @see user_register_submit()
* @see profile2_attach_form()
*/
function profile2_form_before_user_register_submit_handler(&$form, &$form_state) {
global $conf;
// List of available operations during the registration.
$register_ops = array('register_admin_created', 'register_no_approval_required', 'register_pending_approval');
// We also have to track if we change a variables, because
// later we have to restore them.
$changed_ops = &drupal_static('profile2_register_changed_operations', array());
foreach ($register_ops as $op) {
// Save variable value.
if (isset($conf['user_mail_' . $op . '_notify'])) {
$changed_ops['user_mail_' . $op . '_notify'] = $conf['user_mail_' . $op . '_notify'];
}
// Temporary disable the notification about registration.
$conf['user_mail_' . $op . '_notify'] = FALSE;
}
}
/**
* User registration form submit handler
* that executes right after user_register_submit().
*
* This callback sends delayed email notification to a user
* about his registration. See https://drupal.org/node/1097684.
*
* @see profile2_form_prepare_user_register_submit_handler()
* @see user_register_submit()
* @see profile2_attach_form()
*/
function profile2_form_after_user_register_submit_handler(&$form, &$form_state) {
global $conf;
// List of registration operations that where
// notification values were changed.
$changed_ops = &drupal_static('profile2_register_changed_operations', array());
// List of available operations during the registration.
$register_ops = array('register_admin_created', 'register_no_approval_required', 'register_pending_approval');
foreach ($register_ops as $op) {
// If we changed the notification value in
// profile2_form_before_user_register_submit_handler() then change it back.
if (isset($changed_ops['user_mail_' . $op . '_notify'])) {
$conf['user_mail_' . $op . '_notify'] = $changed_ops['user_mail_' . $op . '_notify'];
}
// Otherwise just remove this value from a global variables array.
else {
unset($conf['user_mail_' . $op . '_notify']);
}
}
// Get the values that we need to define which notification
// should be sent to the user. Generally this is a trimmed version
// of user_register_submit() callback.
$admin = !empty($form_state['values']['administer_users']);
$account = $form_state['user'];
$notify = !empty($form_state['values']['notify']);
if ($admin && !$notify) {
// If admin has created a new account and decided to don't notify a user -
// then just do nothing.
}
elseif (!$admin && !variable_get('user_email_verification', TRUE) && $account->status) {
_user_mail_notify('register_no_approval_required', $account);
}
// No administrator approval required.
elseif ($account->status || $notify) {
$op = $notify ? 'register_admin_created' : 'register_no_approval_required';
_user_mail_notify($op, $account);
}
// Administrator approval required.
elseif (!$admin) {
_user_mail_notify('register_pending_approval', $account);
}
}
/**
* Submit handler that builds and saves all profiles in the form.
*
* @see profile2_attach_form()
*/
function profile2_form_submit_handler(&$form, &$form_state) {
profile2_form_submit_build_profile($form, $form_state);
// This is needed as some submit callbacks like user_register_submit() rely on
// clean form values.
profile2_form_submit_cleanup($form, $form_state);
foreach ($form_state['profiles'] as $type => $profile) {
// During registration set the uid field of the newly created user.
if (empty($profile->uid) && isset($form_state['user']->uid)) {
$profile->uid = $form_state['user']->uid;
}
profile2_save($profile);
}
}
/**
* Submit builder. Extracts the form values and updates the profile entities.
*
* @see profile2_attach_form()
*/
function profile2_form_submit_build_profile(&$form, &$form_state) {
foreach ($form_state['profiles'] as $type => $profile) {
// @see entity_form_submit_build_entity()
if (isset($form['profile_' . $type]['#entity_builders'])) {
foreach ($form['profile_' . $type]['#entity_builders'] as $function) {
$function('profile2', $profile, $form['profile_' . $type], $form_state);
}
}
field_attach_submit('profile2', $profile, $form['profile_' . $type], $form_state);
}
}
/**
* Cleans up the form values as the user modules relies on clean values.
*
* @see profile2_attach_form()
*/
function profile2_form_submit_cleanup(&$form, &$form_state) {
foreach ($form_state['profiles'] as $type => $profile) {
unset($form_state['values']['profile_' . $type]);
}
}
/**
* Implements hook_user_categories().
*/
function profile2_user_categories() {
$data = array();
foreach (profile2_get_types() as $type => $info) {
if ($info->userCategory) {
$data[] = array(
'name' => $type,
'title' => $info->getTranslation('label'),
// Add an offset so a weight of 0 appears right of the account category.
'weight' => $info->weight + 3,
'access callback' => 'profile2_category_access',
'access arguments' => array(1, $type)
);
}
}
return $data;
}
/**
* Menu item access callback - check if a user has access to a profile category.
*/
function profile2_category_access($account, $type_name) {
// As there might be no profile yet, create a new object for being able to run
// a proper access check.
$profile = profile2_create(array('type' => $type_name, 'uid' => $account->uid));
return ($account->uid > 0 && $profile->type()->userCategory && profile2_access('edit', $profile));
}
/**
* Determines whether the given user has access to a profile.
*
* @param $op
* The operation being performed. One of 'view', 'update', 'create', 'delete'
* or just 'edit' (being the same as 'create' or 'update').
* @param $profile
* (optional) A profile to check access for. If nothing is given, access for
* all profiles is determined.
* @param $account
* The user to check for. Leave it to NULL to check for the global user.
* @return boolean
* Whether access is allowed or not.
*
* @see hook_profile2_access()
* @see profile2_profile2_access()
*/
function profile2_access($op, $profile = NULL, $account = NULL) {
// Check if profile user has current profile available by role.
if (isset($profile->type)) {
$profile_type = profile2_type_load($profile->type);
if (!empty($profile_type) && !empty($profile_type->data['roles']) && isset($profile->uid)) {
$profile_user = user_load($profile->uid);
$profile_roles = array_keys($profile_type->data['roles']);
$user_roles = array_keys($profile_user->roles);
$matches = array_intersect($profile_roles, $user_roles);
if (empty($matches)) {
return FALSE;
}
}
}
// With access to all profiles there is no need to check further.
if (user_access('administer profiles', $account)) {
return TRUE;
}
if ($op == 'create' || $op == 'update') {
$op = 'edit';
}
// Allow modules to grant / deny access.
$access = module_invoke_all('profile2_access', $op, $profile, $account);
// Only grant access if at least one module granted access and no one denied
// access.
if (in_array(FALSE, $access, TRUE)) {
return FALSE;
}
elseif (in_array(TRUE, $access, TRUE)) {
return TRUE;
}
return FALSE;
}
/**
* Implements hook_profile2_access().
*/
function profile2_profile2_access($op, $profile = NULL, $account = NULL) {
if (isset($profile) && ($type_name = $profile->type)) {
if (user_access("$op any $type_name profile", $account)) {
return TRUE;
}
$account = isset($account) ? $account : $GLOBALS['user'];
if (isset($profile->uid) && $profile->uid == $account->uid && user_access("$op own $type_name profile", $account)) {
return TRUE;
}
}
// Do not explicitly deny access so others may still grant access.
}
/**
* Access callback for the entity API.
*/
function profile2_type_access($op, $type = NULL, $account = NULL) {
return user_access('administer profile types', $account);
}
/**
* Implements hook_theme().
*/
function profile2_theme() {
return array(
'profile2' => array(
'render element' => 'elements',
'template' => 'profile2',
),
);
}
/**
* The class used for profile entities.
*/
class Profile extends Entity {
/**
* The profile id.
*
* @var integer
*/
public $pid;
/**
* The name of the profile type.
*
* @var string
*/
public $type;
/**
* The profile label.
*
* @var string
*/
public $label;
/**
* The user id of the profile owner.
*
* @var integer
*/
public $uid;
/**
* The Unix timestamp when the profile was created.
*
* @var integer
*/
public $created;
/**
* The Unix timestamp when the profile was most recently saved.
*
* @var integer
*/
public $changed;
public function __construct($values = array()) {
if (isset($values['user'])) {
$this->setUser($values['user']);
unset($values['user']);
}
if (isset($values['type']) && is_object($values['type'])) {
$values['type'] = $values['type']->type;
}
if (!isset($values['label']) && isset($values['type']) && $type = profile2_get_types($values['type'])) {
// Initialize the label with the type label, so newly created profiles
// have that as interim label.
$values['label'] = $type->label;
}
parent::__construct($values, 'profile2');
}
/**
* Returns the user owning this profile.
*/
public function user() {
return user_load($this->uid);
}
/**
* Sets a new user owning this profile.
*
* @param $account
* The user account object or the user account id (uid).
*/
public function setUser($account) {
$this->uid = is_object($account) ? $account->uid : $account;
}
/**
* Gets the associated profile type object.
*
* @return ProfileType
*/
public function type() {
return profile2_get_types($this->type);
}
/**
* Returns the full url() for the profile.
*/
public function url() {
$uri = $this->uri();
return url($uri['path'], $uri);
}
/**
* Returns the drupal path to this profile.
*/
public function path() {
$uri = $this->uri();
return $uri['path'];
}
public function defaultUri() {
return array(
'path' => 'user/' . $this->uid,
'options' => array('fragment' => 'profile-' . $this->type),
);
}
public function defaultLabel() {
// Return a label that combines the type name and user name, translatable.
return t('@type profile for @user', array(
'@type' => profile2_get_types($this->type)->getTranslation('label'),
'@user' => format_username($this->user()),
));
}
public function buildContent($view_mode = 'full', $langcode = NULL) {
$content = array();
// Assume newly create objects are still empty.
if (!empty($this->is_new)) {
$content['empty']['#markup'] = '' . t('There is no profile data yet.') . '';
}
return entity_get_controller($this->entityType)->buildContent($this, $view_mode, $langcode, $content);
}
public function save() {
// Don't create a new profile if the user already have one of the same type.
$existing_profile = profile2_load_by_user($this->uid, $this->type);
if (empty($this->pid) && !empty($existing_profile)) {
watchdog('profile2_create', serialize(array(
'message' => 'Profile already exists',
'uid' => $this->uid,
'type' => $this->type,
'path' => current_path(),
'logged_in_user' => $GLOBALS['user']->uid,
)), array(), WATCHDOG_WARNING);
return;
}
// Care about setting created and changed values. But do not automatically
// set a created values for already existing profiles.
if (empty($this->created) && (!empty($this->is_new) || !$this->pid)) {
$this->created = REQUEST_TIME;
}
$this->changed = REQUEST_TIME;
// Clear the static cache from profile2_load_by_user() before saving, so
// that profiles are correctly loaded in insert/update hooks.
$cache = &drupal_static('profile2_load_by_user', array());
unset($cache[$this->uid]);
return parent::save();
}
}
/**
* Use a separate class for profile types so we can specify some defaults
* modules may alter.
*/
class ProfileType extends Entity {
/**
* Whether the profile type appears in the user categories.
*/
public $userCategory = TRUE;
/**
* Whether the profile is displayed on the user account page.
*/
public $userView = TRUE;
public $type;
public $label;
public $weight = 0;
public function __construct($values = array()) {
parent::__construct($values, 'profile2_type');
}
/**
* Returns whether the profile type is locked, thus may not be deleted or renamed.
*
* Profile types provided in code are automatically treated as locked, as well
* as any fixed profile type.
*/
public function isLocked() {
return isset($this->status) && empty($this->is_new) && (($this->status & ENTITY_IN_CODE) || ($this->status & ENTITY_FIXED));
}
/**
* Overridden, to introduce the method for old entity API versions (BC).
*
* @todo Remove once we bump the required entity API version.
*/
public function getTranslation($property, $langcode = NULL) {
if (module_exists('profile2_i18n')) {
return parent::getTranslation($property, $langcode);
}
return $this->$property;
}
/**
* Overrides Entity::save().
*/
public function save() {
parent::save();
// Clear field info caches such that any changes to extra fields get
// reflected.
field_info_cache_clear();
}
}
/**
* View a profile.
*
* @see Profile::view()
*/
function profile2_view($profile, $view_mode = 'full', $langcode = NULL, $page = NULL) {
return $profile->view($view_mode, $langcode, $page);
}
/**
* Implements hook_form_FORMID_alter().
*
* Adds a checkbox for controlling field view access to fields added to
* profiles.
*/
function profile2_form_field_ui_field_edit_form_alter(&$form, &$form_state) {
if (!empty($form['instance']['entity_type']['#value']) && $form['instance']['entity_type']['#value'] == 'profile2') {
$form['field']['settings']['profile2_private'] = array(
'#type' => 'checkbox',
'#title' => t('Make the content of this field private.'),
'#default_value' => !empty($form['#field']['settings']['profile2_private']),
'#description' => t('If checked, the content of this field is only shown to the profile owner and administrators.'),
);
}
else {
// Add the value to the form so it isn't lost.
$form['field']['settings']['profile2_private'] = array(
'#type' => 'value',
'#value' => !empty($form['#field']['settings']['profile2_private']),
);
}
}
/**
* Implements hook_field_access().
*/
function profile2_field_access($op, $field, $entity_type, $profile = NULL, $account = NULL) {
if ($entity_type == 'profile2' && $op == 'view' && isset($profile)) {
// Check if the profile type is accessible (e.g. applicable to the role).
if (!profile2_access($op, $profile, $account)) {
return FALSE;
}
// Deny view access, if someone else views a private field.
if (!empty($field['settings']['profile2_private']) && !user_access('administer profiles', $account)) {
$account = isset($account) ? $account : $GLOBALS['user'];
if ($account->uid != $profile->uid) {
return FALSE;
}
}
}
}
/**
* Implements hook_field_extra_fields().
*
* We need to add pseudo fields for profile types to allow for weight settings
* when viewing a user or filling in the profile types while registrating.
*/
function profile2_field_extra_fields() {
$extra = array();
foreach (profile2_get_types() as $type_name => $type) {
// Appears on: admin/config/people/accounts/display
if (!empty($type->userView)) {
$extra['user']['user']['display']['profile_' . $type_name] = array(
'label' => t('Profile: @profile', array('@profile' => $type->label)),
'weight' => $type->weight,
);
}
// Appears on: admin/config/people/accounts/fields
if (!empty($type->data['registration'])) {
$extra['user']['user']['form']['profile_' . $type_name] = array(
'label' => t('Profile: @profile', array('@profile' => $type->label)),
'description' => t('Appears during registration only.'),
'weight' => $type->weight,
);
}
}
return $extra;
}
/**
* Entity metadata callback to load profiles for the given user account.
*/
function profile2_user_get_properties($account, array $options, $name) {
// Remove the leading 'profile_' from the property name to get the type name.
$profile = profile2_load_by_user($account, substr($name, 8));
return $profile ? $profile : NULL;
}
/**
* Implements hook_ctools_plugin_directory().
*/
function profile2_ctools_plugin_directory($owner, $plugin_type) {
if ($owner == 'ctools' && !empty($plugin_type)) {
return 'plugins/' . $plugin_type;
}
}
/**
* Implements hook_preprocess_ctools_context_item_form().
*
* When the User context is added, CTools will update the relationship dropdown
* with ajax. The dropdown is passed through theme_ctools_context_item_form
* before being passed to ajax_render, so that is our best opportunity to
* alter it.
*
* @see ctools_context_ajax_item_add
*/
function profile2_preprocess_ctools_context_item_form(&$vars) {
unset($vars['form']['buttons']['relationship']['item']['#options']['entity_from_schema:uid-user-profile2']);
}
/**
* Determines whether the given user has access to delete a profile.
*/
function profile2_delete_access($uid, $type_name) {
$profile = profile2_by_uid_load($uid, $type_name);
return is_object($profile) ? profile2_access('edit', $profile) : FALSE;
}
/**
* Menu load callback.
*
* Returns the profile object for the given user. If there is none yet, a new
* object is created.
*/
function profile2_by_uid_load($uid, $type_name) {
if ($uid && is_numeric($uid) && ($account = user_load($uid))) {
$profile = profile2_load_by_user($account, $type_name);
if (!$profile) {
$profile = profile2_create(array('type' => $type_name));
$profile->setUser($account);
$profile->is_new = TRUE;
}
return $profile;
}
return FALSE;
}
/**
* Implements hook_preprocess_page().
*
* Fix the page titles on the profile2 edit tabs.
* We want the titles to be the full profile label, giving the user name & profile name.
*/
function profile2_preprocess_page(&$vars) {
// This is true when editing a profile in a tab.
if (!empty($vars['page']['content']['system_main']['#user_category'])) {
$ptype = $vars['page']['content']['system_main']['#user_category'];
if (!empty($vars['page']['content']['system_main']["profile_$ptype"])) {
$item = $vars['page']['content']['system_main']["profile_$ptype"];
// If we've found an item, and it has a profile2 entity, display the title.
if (!empty($item['#entity'])) {
$vars['title'] = $item['#entity']->label();
}
}
}
}