reverted: --- b/lib/Drupal/profile2/Profile.php +++ /dev/null @@ -1,158 +0,0 @@ -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() { - if (module_exists('profile2_i18n')) { - // Run the label through i18n_string() using the profile2_type label - // context, so the default label (= the type's label) gets translated. - return entity_i18n_string('profile2:profile2_type:' . $this->type . ':label', $this->label); - } - return $this->label; - } - - 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() { - // 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; - - parent::save(); - // Update the static cache from profile2_load_by_user(). - $cache = &drupal_static('profile2_load_by_user', array()); - if (isset($cache[$this->uid])) { - $cache[$this->uid][$this->type] = $this->pid; - } - } -} reverted: --- b/lib/Drupal/profile2/ProfileType.php +++ /dev/null @@ -1,60 +0,0 @@ -$property; - } -} diff -u b/profile2.admin.inc b/profile2.admin.inc --- b/profile2.admin.inc +++ b/profile2.admin.inc @@ -102,25 +102,67 @@ /** - * Form constructor to delete a ProfileType object. - * - * @param Drupal\profile2\ProfileType $profile_type - * The ProfileType object to delete. + * Form constructor to import a ProfileType object. */ -function profile2_type_delete_form($form, &$form_state, ProfileType $profile_type) { - $form_state['profile_type'] = $profile_type; - - $form['id'] = array('#type' => 'value', '#value' => $profile_type->id()); - return confirm_form($form, - format_string('Are you sure you want to delete %label', array('%label' => $profile_type->label())), - 'admin/structure/profiles', - NULL, - 'Delete' +function profile2_type_import_form($form, &$form_state) { + $form['import'] = array( + '#type' => 'textarea', + '#title' => t('Import'), + '#description' => t('Paste an exported %entity_type here.', array('%entity_type' => $this->entityInfo['label'])), + '#rows' => 20, + ); + $form['overwrite'] = array( + '#title' => t('Overwrite'), + '#type' => 'checkbox', + '#description' => t('If checked, any existing %entity with the same identifier will be replaced by the import.', array('%entity' => $this->entityInfo['label'])), + '#default_value' => FALSE, ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Import'), + ); + return $form; +} + +/** + * Form submission handler for profile_type_import_form(). + * + * @todo Fill in import function (see ..) + */ + function profile2_type_import_form_submit($form, &$form_state) { +if ($entity = entity_import($this->entityType, $form_state['values']['import'])) { + // Store the successfully imported entity in $form_state. + $form_state[$this->entityType] = $entity; + if (!$form_state['values']['overwrite']) { + // Check for existing entities with the same identifier. + $id = entity_id($this->entityType, $entity); + $entities = entity_load($this->entityType, array($id)); + if (!empty($entities)) { + $label = entity_label($this->entityType, $entity); + $vars = array('%entity' => $this->entityInfo['label'], '%label' => $label); + form_set_error('import', t('Import of %entity %label failed, a %entity with the same machine name already exists. Check the overwrite option to replace it.', $vars)); + } + } + } + else { + form_set_error('import', t('Import failed.')); + } } /** - * Form submission handler for profile_type_delete_form(). + * Form constructor to export a ProfileType object. + * + * @param Drupal\profile2\ProfileType $profile_type + * The ProfileType object to export. + * + * @todo Fill in export function (see ..) */ -function profile2_type_delete_form_submit($form, &$form_state) { - $form_state['profile_type']->delete(); - $form_state['redirect'] = 'admin/structure/profiles'; +function profile2_type_export_form($form, &$form_state, ProfileType $profile_type) { + // generate $export + $form['export'] = array( + '#type' => 'textarea', + '#title' => t('Export'), + '#description' => t('For importing copy the content of the text area and paste it into the import page.'), + '#rows' => 25, + '#default_value' => $export, + ); + return $form; } diff -u b/profile2.module b/profile2.module --- b/profile2.module +++ b/profile2.module @@ -22,10 +22,10 @@ 'bundle' => 'type', 'label' => 'label', ), + 'bundles' => array(), 'bundle keys' => array( 'bundle' => 'type', ), - 'bundles' => array(), 'view modes' => array( 'account' => array( 'label' => t('User account'), @@ -36,6 +36,24 @@ ), ); + // 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; @@ -69,7 +87,7 @@ * @param Drupal\profile2\Profile $profile * A Profile entity. */ -function profile2_profile_uri(Profile $profile) { +function profile2_profile_uri(Drupal\profile2\Profile $profile) { return array( 'path' => 'admin/structure/profiles', ); @@ -81,7 +99,7 @@ * @param Drupal\profile2\ProfileType $profile_type * A ProfileType entity. */ -function profile2_profile_type_uri(ProfileType $profile) { +function profile2_profile_type_uri(Drupal\profile2\ProfileType $profile_type) { return array( 'path' => 'admin/structure/profiles/manage', ); @@ -94,6 +112,7 @@ $items = array(); $items['admin/structure/profiles'] = array( 'title' => 'Profile types', + 'description' => 'Manage profiles, including fields.', 'page callback' => 'profile2_type_list_page', 'access callback' => TRUE, ); @@ -108,6 +127,14 @@ 'page arguments' => array('profile2_type_form'), 'access callback' => TRUE, 'type' => MENU_LOCAL_ACTION, + 'file' => 'profile2.admin.inc', + ); + $items['admin/structure/profiles/import'] = array( + 'title' => 'Import profile type', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('profile2_type_import_form'), + 'access callback' => TRUE, + 'file' => 'profile2.admin.inc', ); $items['admin/structure/profiles/manage/%profile2_type'] = array( 'title' => 'Edit profile type', @@ -121,13 +148,21 @@ 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); + $items['admin/structure/profiles/manage/%profile2_type/fields'] = array( + 'title' => 'Manage profile fields', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('field_ui_field_overview_form', 'profile2', 4), + 'access callback' => TRUE, + 'file' => 'field_ui.admin.inc', + 'file path' => drupal_get_path('module', 'field_ui'), + 'type' => MENU_LOCAL_ACTION + ); $items['admin/structure/profiles/manage/%profile2_type/delete'] = array( 'title' => 'Delete profile type', 'page callback' => 'drupal_get_form', 'page arguments' => array('profile2_type_delete_form', 4), 'access callback' => TRUE, 'file' => 'profile2.admin.inc', - 'type' => MENU_LOCAL_ACTION ); $items['admin/structure/profiles/manage/%profile2_type/clone'] = array( 'title' => 'Clone profile type', @@ -136,6 +171,13 @@ 'access callback' => TRUE, 'file' => 'profile2.admin.inc', ); + $items['admin/structure/profiles/manage/%profile2_type/export'] = array( + 'title' => 'Clone profile type', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('profile2_type_export_form', 4), + 'access callback' => TRUE, + 'file' => 'profile2.admin.inc', + ); return $items; } @@ -145,7 +187,7 @@ */ function profile2_type_list_page() { $rows = array(); - foreach (entity_load_multiple('profile2_type', FALSE) as $profile) { + foreach (entity_load_multiple('profile2_type') as $profile) { $uri = $profile->uri(); $row = array(); $row[] = $profile->label(); @@ -255,10 +297,12 @@ * Depending whether $type isset, an array of profile types or a single one. */ function profile2_get_types($type_name = NULL) { - $types = entity_load_multiple('profile2_type'); - foreach ($types as $key => $type) { - if ($type->get('type') != $type_name) { - unset($types[$key]); + $types = entity_load_multiple('profile2_type', NULL, TRUE); + if (!is_null($type_name)) { + foreach ($types as $key => $type) { + if ($type->get('type') != $type_name) { + unset($types[$key]); + } } } return isset($type_name) ? reset($types) : $types; @@ -686,6 +730,201 @@ } /** + * 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() { + if (module_exists('profile2_i18n')) { + // Run the label through i18n_string() using the profile2_type label + // context, so the default label (= the type's label) gets translated. + return entity_i18n_string('profile2:profile2_type:' . $this->type . ':label', $this->label); + } + return $this->label; + } + + 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() { + // 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; + + parent::save(); + // Update the static cache from profile2_load_by_user(). + $cache = &drupal_static('profile2_load_by_user', array()); + if (isset($cache[$this->uid])) { + $cache[$this->uid][$this->type] = $this->pid; + } + } +} + +/** + * 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; + } +} + +/** * View a profile. * * @see Profile::view() only in patch2: unchanged: --- a/lib/Drupal/profile2/Profile.php +++ b/lib/Drupal/profile2/Profile.php @@ -57,7 +57,7 @@ class Profile extends Entity { public $changed; - public function __construct($values = array()) { + public function __construct(array $values = array(), $entity_type = 'profile2') { if (isset($values['user'])) { $this->setUser($values['user']); unset($values['user']); @@ -70,7 +70,7 @@ class Profile extends Entity { // have that as interim label. $values['label'] = $type->label; } - parent::__construct($values, 'profile2'); + parent::__construct($values, $entity_type); } /** @@ -156,4 +156,3 @@ class Profile extends Entity { } } } - only in patch2: unchanged: --- a/lib/Drupal/profile2/ProfileType.php +++ b/lib/Drupal/profile2/ProfileType.php @@ -29,8 +29,8 @@ class ProfileType extends Entity { public $label; public $weight = 0; - public function __construct($values = array()) { - parent::__construct($values, 'profile2_type'); + public function __construct(array $values = array(), $entity_type = 'profile2_type') { + parent::__construct($values, $entity_type); } /** @@ -38,9 +38,12 @@ class ProfileType extends Entity { * * Profile types provided in code are automatically treated as locked, as well * as any fixed profile type. + * + * @todo Remove method or make entity status available. */ public function isLocked() { - return isset($this->status) && empty($this->is_new) && (($this->status & ENTITY_IN_CODE) || ($this->status & ENTITY_FIXED)); + // At the moment there is not status available, so profile type can't be locked. + return false; } /** only in patch2: unchanged: --- a/profile2.install +++ b/profile2.install @@ -12,6 +12,7 @@ function profile2_install() { // Add an initial profile type, but only if installed manually. In case the // module is installed via an installation profile, skip that. if (!drupal_installation_attempted()) { + entity_info_cache_clear(); $type = entity_create('profile2_type', array( 'type' => 'main', 'label' => t('Main profile'),