diff --git a/config/realname.settings.yml b/config/realname.settings.yml new file mode 100644 index 0000000..2711abc --- /dev/null +++ b/config/realname.settings.yml @@ -0,0 +1 @@ +pattern: '[user:name-raw]' diff --git a/lib/Drupal/realname/Controller/RealnameAutocompleteController.php b/lib/Drupal/realname/Controller/RealnameAutocompleteController.php new file mode 100644 index 0000000..7c43eee --- /dev/null +++ b/lib/Drupal/realname/Controller/RealnameAutocompleteController.php @@ -0,0 +1,24 @@ +get('realname.autocomplete') + ); + } + +} diff --git a/lib/Drupal/realname/Form/SettingsForm.php b/lib/Drupal/realname/Form/SettingsForm.php new file mode 100644 index 0000000..bed5f4f --- /dev/null +++ b/lib/Drupal/realname/Form/SettingsForm.php @@ -0,0 +1,83 @@ +configFactory->get('realname.settings'); + + + $form['pattern'] = array( + '#type' => 'textfield', + '#title' => t('Realname pattern'), + '#default_value' => $config->get('pattern'), + // @todo Implement token_element_validate, if token module will be ported to D8 + /* + '#element_validate' => array('token_element_validate'), + '#token_types' => array('user'), + */ + '#min_tokens' => 1, + '#required' => TRUE, + '#maxlength' => 256, + ); + + // Add the token tree UI. + // @todo Implementation of token_help, if token module will not be not ported to 8.x + /* + $form['token_help'] = array( + '#theme' => 'token_help', + '#token_types' => array('user'), + '#global_types' => FALSE, + ); + */ + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, array &$form_state) { + if (strpos($form_state['input']['pattern'], '[user:name]') !== FALSE) { + form_set_error('realname_pattern', t('The [user:name] token cannot be used as it will cause recursion.')); + } + + parent::validateForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, array &$form_state) { + if ($form['pattern']['#default_value'] != $form_state['values']['pattern']) { + $config = $this->configFactory->get('realname.settings'); + $config->set('pattern', $form_state['values']['pattern'])->save(); + // Only clear the realname cache if the pattern was changed. + realname_delete_all(); + }; + + parent::submitForm($form, $form_state); + } + +} diff --git a/lib/Drupal/realname/Plugin/Action/UpdateRealname.php b/lib/Drupal/realname/Plugin/Action/UpdateRealname.php new file mode 100644 index 0000000..e60ab75 --- /dev/null +++ b/lib/Drupal/realname/Plugin/Action/UpdateRealname.php @@ -0,0 +1,30 @@ +id()); + } + +} diff --git a/lib/Drupal/realname/RealnameAutocomplete.php b/lib/Drupal/realname/RealnameAutocomplete.php new file mode 100644 index 0000000..3996212 --- /dev/null +++ b/lib/Drupal/realname/RealnameAutocomplete.php @@ -0,0 +1,46 @@ +configFactory->get('user.settings')->get('anonymous'); + // Allow autocompletion for the anonymous user. + if (stripos($anonymous_name, $string) !== FALSE) { + $matches[$anonymous_name] = check_plain($anonymous_name); + } + } + + $query = $this->connection->select('users'); + $query->join('realname', 'rn', 'users.uid = rn.uid'); + + $query + ->fields('users', array('name')) + ->fields('rn', array('realname')) + ->condition('rn.realname', db_like($string) . '%', 'LIKE') + ->range(0, 10); + + $result = $query->execute(); + foreach ($result as $account) { + $matches[$account->name] = check_plain($account->realname); + } + } + + return $matches; + } + +} diff --git a/lib/Drupal/realname/RealnameServiceProvider.php b/lib/Drupal/realname/RealnameServiceProvider.php new file mode 100644 index 0000000..23a8d16 --- /dev/null +++ b/lib/Drupal/realname/RealnameServiceProvider.php @@ -0,0 +1,33 @@ +getDefinition('user.autocomplete'); + $definition->setClass('Drupal\realname\RealnameAutocomplete'); + */ + } + +} diff --git a/realname.admin.inc b/realname.admin.inc deleted file mode 100644 index d88010c..0000000 --- a/realname.admin.inc +++ /dev/null @@ -1,54 +0,0 @@ - 'textfield', - '#title' => t('Realname pattern'), - '#default_value' => variable_get('realname_pattern', '[user:name-raw]'), - '#element_validate' => array('token_element_validate'), - '#token_types' => array('user'), - '#min_tokens' => 1, - '#required' => TRUE, - '#maxlength' => 256, - ); - - // Add the token tree UI. - $form['token_help'] = array( - '#theme' => 'token_tree', - '#token_types' => array('user'), - '#global_types' => FALSE, - '#dialog' => TRUE, - ); - - $form['#submit'][] = 'realname_settings_form_submit'; - return system_settings_form($form); -} - -/** - * Form validation handler for realname_settings_form(). - * - * @see realname_settings_form_submit() - */ -function realname_settings_form_validate($form, &$form_state) { - if (strpos($form_state['values']['realname_pattern'], '[user:name]') !== FALSE) { - form_set_error('realname_pattern', t('The [user:name] token cannot be used as it will cause recursion.')); - } -} - -/** - * Form submission handler for realname_settings_form(). - * - * @see realname_settings_form_validate() - */ -function realname_settings_form_submit($form, $form_state) { - if ($form['realname_pattern']['#default_value'] != $form_state['values']['realname_pattern']) { - // Only clear the realname cache if the pattern was changed. - realname_delete_all(); - } -} diff --git a/realname.info b/realname.info deleted file mode 100644 index ae9a14d..0000000 --- a/realname.info +++ /dev/null @@ -1,11 +0,0 @@ -name = Real name -description = Provides token-based name displays for users. -core = 7.x -dependencies[] = token -files[] = realname.module -files[] = realname.admin.inc -files[] = realname.pages.inc -files[] = realname.tokens.inc -files[] = realname.test -files[] = realname.install -configure = admin/config/people/realname diff --git a/realname.info.yml b/realname.info.yml new file mode 100644 index 0000000..c22b073 --- /dev/null +++ b/realname.info.yml @@ -0,0 +1,6 @@ +name: 'Real name' +type: module +description: 'Provides token-based name displays for users.' +configure: admin/config/people/realname +core: 8.x +version: VERSION diff --git a/realname.install b/realname.install index 926f0f0..f229aa2 100644 --- a/realname.install +++ b/realname.install @@ -44,78 +44,4 @@ function realname_schema() { return $schema; } -/** - * Implements hook_requirements(). - */ -function realname_requirements($phase) { - $requirements = array(); - - if ($phase == 'runtime') { - if (!strpos(variable_get('user_mail_register_admin_created_body', FALSE), '[user:name-raw]')) { - $requirements['user_mail_register_admin_created_body'] = array( - 'title' => t('E-mail: "Welcome (new user created by administrator)" template'), - 'value' => t('Not configured'), - 'severity' => REQUIREMENT_ERROR, - 'description' => t('Replace the token [user:name] with [user:name-raw] in e-mail template.', array('@people' => url('admin/config/people/accounts', array('fragment' => 'edit-email-admin-created')))), - ); - } - if (!strpos(variable_get('user_mail_register_no_approval_required_body', FALSE), '[user:name-raw]')) { - $requirements['user_mail_register_no_approval_required_body'] = array( - 'title' => t('E-mail: "Welcome (no approval required)" template'), - 'value' => t('Not configured'), - 'severity' => REQUIREMENT_ERROR, - 'description' => t('Replace the token [user:name] with [user:name-raw] in e-mail template.', array('@people' => url('admin/config/people/accounts', array('fragment' => 'edit-email-no-approval-required')))), - ); - } - if (!strpos(variable_get('user_mail_status_activated_body', FALSE), '[user:name-raw]')) { - $requirements['user_mail_status_activated_body'] = array( - 'title' => t('E-mail: "Account activation" template'), - 'value' => t('Not configured'), - 'severity' => REQUIREMENT_ERROR, - 'description' => t('Replace the token [user:name] with [user:name-raw] in e-mail template.', array('@people' => url('admin/config/people/accounts', array('fragment' => 'edit-email-activated')))), - ); - } - } - - return $requirements; -} - -/** - * Implements hook_disable(). - */ -function realname_disable() { - drupal_set_message('You need to replace the token [user:name-raw] with [user:name] in e-mail templates.', 'warning'); -} - -/** - * Implements hook_uninstall(). - */ -function realname_uninstall() { - variable_del('realname_pattern'); -} - -/** - * Update the {realname} table for Drupal 7. - */ -function realname_update_7000() { - // Add the {realname}.created field if it doesn't already exist. - if (!db_field_exists('realname', 'created')) { - $fields['created'] = array( - 'description' => 'The UNIX timestamp of when the real name was created.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ); - db_add_field('realname', 'created', $fields['created']); - } - - $fields['realname'] = array( - 'description' => 'The generated real name of the user.', - 'type' => 'varchar', - 'length' => '255', - 'not null' => TRUE, - 'default' => '', - ); - db_change_field('realname', 'realname', 'realname', $fields['realname']); -} +// @todo: Implement update hooks. diff --git a/realname.module b/realname.module index e21a1cd..9073e71 100644 --- a/realname.module +++ b/realname.module @@ -1,4 +1,5 @@ array( - 'title' => t('Administer Real Name'), - 'description' => t('Allows users to administer realname settings.'), - ), + $permissions['administer realname'] = array( + 'title' => t('Administer Real Name configuration.'), ); + return $permissions; } /** @@ -32,50 +31,23 @@ function realname_menu() { $items['admin/config/people/realname'] = array( 'title' => 'Real name', 'description' => 'Use tokens to configure how user names are displayed.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('realname_settings_form'), - 'access arguments' => array('administer realname'), - 'file' => 'realname.admin.inc', - ); - - $items['realname/autocomplete'] = array( - 'page callback' => 'realname_autocomplete', - 'page arguments' => array('', '', 2), - 'access arguments' => array('access user profiles'), - 'type' => MENU_CALLBACK, - ); - $items['realname/autocomplete/%/%/%'] = array( - 'page callback' => 'realname_autocomplete', - 'page arguments' => array(2, 3, 4), - 'access callback' => 'realname_autocomplete_access_callback', - 'access arguments' => array(2, 3, 4), - 'type' => MENU_CALLBACK, + 'route_name' => 'realname_settings', ); return $items; } /** - * Implements hook_menu_alter() to replace user autocomplete with realname autocomplete. - */ -function realname_menu_alter(&$items) { - // Implement realname autocomplete. - $items['user/autocomplete']['page callback'] = 'realname_autocomplete'; - $items['user/autocomplete']['page arguments'] = array('', '', 2); -} - -/** - * Implements hook_username_alter(). + * Implements hook_user_format_name_alter(). */ -function realname_username_alter(&$name, $account) { +function realname_user_format_name_alter(&$name, $account) { static $in_username_alter = FALSE; + $uid = $account->id(); - if (empty($account->uid)) { - // Don't alter anonymous users or objects that do not have any user ID. - return; - } + // Don't alter anonymous users or objects that do not have any user ID. + if(empty($uid)) return; - // Real name was loaded/generated via hook_user_load(), so re-use it. + // Real name was loaded/generated via hook_user_load(), so re-use it. if (isset($account->realname)) { if (drupal_strlen($account->realname)) { // Only if the real name is a non-empty string is $name actually altered. @@ -91,8 +63,8 @@ function realname_username_alter(&$name, $account) { // If $account->realname was undefined, then the user account object was // not properly loaded. We must enforce calling user_load(). - if ($realname_account = user_load($account->uid)) { - realname_username_alter($name, $realname_account); + if ($realname_account = user_load($uid)) { + realname_user_format_name_alter($name, $realname_account); } $in_username_alter = FALSE; @@ -104,24 +76,26 @@ function realname_username_alter(&$name, $account) { */ function realname_user_load(array $accounts) { $realnames = realname_load_multiple($accounts); + foreach ($realnames as $uid => $realname) { $accounts[$uid]->realname = $realname; } } + /** * Implements hook_user_update(). */ -function realname_user_update(array &$edit, stdClass $account, $category) { - // Since user data may have changed, update the realname. - realname_update($account); +function realname_user_update($account) { + // Since user data may have changed, delete the existing realname. + realname_delete($account->id()); } /** * Implements hook_user_delete(). */ -function realname_user_delete(stdClass $account) { - realname_delete($account->uid); +function realname_user_delete($account) { + realname_delete($account->id()); } /** @@ -144,58 +118,11 @@ function realname_user_operations() { function realname_user_operations_realname_update(array $uids) { $accounts = user_load_multiple($uids); foreach ($accounts as $account) { - realname_update($account); + realname_delete($account->id()); } } /** - * Implements hook_action_info(). - */ -function realname_action_info() { - $info['realname_action_realname_update'] = array( - 'type' => 'user', - 'label' => t('Update real name'), - 'configurable' => FALSE, - ); - - return $info; -} - -/** - * Implements hook_rules_action_info(). - */ -function realname_rules_action_info() { - $items['user_realname_update'] = array( - 'label' => t('Update real name'), - 'base' => 'realname_action_realname_update', - 'parameter' => array( - 'account' => array( - 'type' => 'user', - 'label' => t('User'), - 'save' => FALSE, - ), - ), - 'group' => t('User'), - 'access callback' => 'rules_user_integration_access', - ); - return $items; -} - -/** - * Action callback to update a user's realname. - */ -function realname_action_realname_update($account, $context = array()) { - realname_update($account); -} - -/** - * Implements hook_views_api(). - */ -function realname_views_api() { - return array('api' => 3); -} - -/** * @addtogroup realname * @{ */ @@ -209,7 +136,8 @@ function realname_views_api() { * The user's generated real name. */ function realname_load(stdClass $account) { - $realnames = realname_load_multiple(array($account->uid => $account)); + $realnames = realname_load_multiple(array($account->id() => $account)); + return reset($realnames); } @@ -226,6 +154,7 @@ function realname_load_multiple(array $accounts) { if ($new_accounts = array_diff_key($accounts, $realnames)) { // Attempt to fetch realnames from the database first. + $realnames += db_query("SELECT uid, realname FROM {realname} WHERE uid IN (:uids)", array(':uids' => array_keys($new_accounts)))->fetchAllKeyed(); // For each account that was not present in the database, generate its @@ -250,13 +179,14 @@ function realname_load_multiple(array $accounts) { * @see hook_realname_alter() * @see hook_realname_update() */ -function realname_update($account) { - // Get the default pattern and allow other modules to alter it. - $pattern = variable_get('realname_pattern', '[user:name-raw]'); - drupal_alter('realname_pattern', $pattern, $account); +function realname_update(User $account) { + // Get the default pattern and allow other modules to alter it. + $config = config('realname.settings'); + $pattern = $config->get('pattern'); + \Drupal::moduleHandler()->alter('realname_pattern', $pattern, $account); // Perform token replacement on the real name pattern. - $realname = token_replace($pattern, array('user' => $account), array('clear' => TRUE, 'sanitize' => FALSE)); + $realname = \Drupal::token()->replace($pattern, array('user' => $account), array('clear' => TRUE, 'sanitize' => FALSE)); // Remove any HTML tags. $realname = strip_tags(decode_entities($realname)); @@ -268,11 +198,11 @@ function realname_update($account) { $realname = truncate_utf8(trim($realname), 255); // Allow other modules to alter the generated realname. - drupal_alter('realname', $realname, $account); + \Drupal::moduleHandler()->alter('realname', $realname, $account); // Save to the database and the static cache. db_merge('realname') - ->key(array('uid' => $account->uid)) + ->key(array('uid' => $account->id())) ->fields(array( 'realname' => $realname, 'created' => REQUEST_TIME, @@ -280,9 +210,9 @@ function realname_update($account) { ->execute(); // Allow modules to react to the realname being updated. - module_invoke_all('realname_update', $realname, $account); + \Drupal::moduleHandler()->invokeAll('realname_update', array($realname, $account)); - $account->realname = $realname; + $account->setUsername($realname); return $realname; } @@ -312,7 +242,7 @@ function realname_delete_multiple(array $uids) { * Delete all real names. */ function realname_delete_all() { - db_truncate('realname')->execute(); + db_delete('realname')->execute(); drupal_static_reset('realname_load_multiple'); entity_get_controller('user')->resetCache(); } @@ -320,119 +250,3 @@ function realname_delete_all() { /** * @} End of "addtogroup realname". */ - -/** - * Menu callback; Retrieve a JSON object containing autocomplete suggestions - * for existing users based on their generated real names. - */ -function realname_autocomplete($field_name, $entity_type, $bundle_name, $string = '') { - $instance = field_info_instance($entity_type, $field_name, $bundle_name); - $matches = array(); - - // User entityreference autocomplete fields. - if ($instance['widget']['type'] == 'entityreference_autocomplete' && !empty($string)) { - $query = db_select('users', 'u'); - $query->leftJoin('realname', 'rn', 'u.uid = rn.uid'); - $query->fields('u', array('uid', 'name')); - if ($instance['widget']['settings']['match_operator'] == 'CONTAINS') { - $query->condition(db_or() - ->condition('rn.realname', '%' . db_like($string) . '%', 'LIKE') - ->condition('u.name', '%' . db_like($string) . '%', 'LIKE') - ); - } - else { - $query->condition(db_or() - ->condition('rn.realname', db_like($string) . '%', 'LIKE') - ->condition('u.name', db_like($string) . '%', 'LIKE') - ); - } - $query->range(0, 10); - $uids = $query->execute()->fetchCol(); - $accounts = user_load_multiple($uids); - - foreach ($accounts as $account) { - if ($matches[$account->name] != $account->name) { - $matches[t('!account (@uid)', array('!account' => format_username($account), '@uid' => $account->uid))] = t('!realname (@username)', array('!realname' => format_username($account), '@username' => $account->name)); - } - } - } - // User autocomplete fields. - elseif (!empty($string)) { - $query = db_select('users', 'u'); - $query->leftJoin('realname', 'rn', 'u.uid = rn.uid'); - $query->fields('u', array('uid', 'name')); - $query->condition(db_or() - ->condition('rn.realname', db_like($string) . '%', 'LIKE') - ->condition('u.name', db_like($string) . '%', 'LIKE') - ); - $query->range(0, 10); - $uids = $query->execute()->fetchCol(); - $accounts = user_load_multiple($uids); - - foreach ($accounts as $account) { - if ($matches[$account->name] != $account->name) { - $matches[$account->name] = t('!realname (@username)', array('!realname' => format_username($account), '@username' => $account->name)); - } - } - } - - drupal_json_output($matches); -} - -/** - * Menu Access callback for the autocomplete widget. - * - * @param $field_name - * The name of the entity-reference field. - * @param $entity_type - * The entity type. - * @param $bundle_name - * The bundle name. - * @return - * True if user can access this menu item. - */ -function realname_autocomplete_access_callback($field_name, $entity_type, $bundle_name) { - $field = field_info_field($field_name); - $instance = field_info_instance($entity_type, $field_name, $bundle_name); - - if (!$field || !$instance || $field['type'] != 'entityreference' || !field_access('edit', $field, $entity_type)) { - return FALSE; - } - return TRUE; -} - -/** - * Implements hook_widget_form_alter(). - * - * Overrides entityreference autocomplete fields that point to an entity - * of type 'user'. - */ -function realname_field_widget_form_alter(&$element, &$form_state, $context) { - if ($context['field']['type'] == 'entityreference') { - if ($context['field']['settings']['target_type'] == 'user') { - $element['target_id']['#autocomplete_path'] = 'realname/autocomplete/' . $context['field']['field_name'] . '/' . $context['instance']['entity_type'] . '/' . $context['instance']['bundle']; - } - } -} - -/** - * Implements hook_form_FORM_ID_alter(). - */ -function realname_form_user_admin_settings_alter(&$form, &$form_state, $form_id) { - $form['email_admin_created']['user_mail_register_admin_created_body']['#element_validate'][] = 'realname_username_raw_token_validate'; - $form['email_no_approval_required']['user_mail_register_no_approval_required_body']['#element_validate'][] = 'realname_username_raw_token_validate'; - $form['email_activated']['settings']['user_mail_status_activated_body']['#element_validate'][] = 'realname_username_raw_token_validate'; -} - -/** - * E-mail settings form validation callback. - */ -function realname_username_raw_token_validate(&$element, &$form_state) { - $value = isset($element['#value']) ? $element['#value'] : $element['#default_value']; - - if (!strpos($value, '[user:name-raw]')) { - form_error($element, t('The %element-title does not contain [user:name-raw] token.', array('%element-title' => $element['#title']))); - } - - return $element; -} diff --git a/realname.routing.yml b/realname.routing.yml new file mode 100644 index 0000000..b40538c --- /dev/null +++ b/realname.routing.yml @@ -0,0 +1,13 @@ +realname_settings: + path: '/admin/config/people/realname' + defaults: + _form: 'Drupal\realname\Form\SettingsForm' + requirements: + _permission: 'administer realname' + +realname.autocomplete: + path: '/realname/autocomplete' + defaults: + _controller: '\Drupal\realname\Controller\RealnameAutocompleteController::autocompleteUser' + requirements: + _permission: 'access user profiles' diff --git a/realname.services.yml b/realname.services.yml new file mode 100644 index 0000000..2182edf --- /dev/null +++ b/realname.services.yml @@ -0,0 +1,4 @@ +services: + realname.autocomplete: + class: Drupal\realname\RealnameAutocomplete + arguments: ['@database', '@config.factory'] diff --git a/realname.tokens.inc b/realname.tokens.inc index 1bbe9f2..8709c91 100644 --- a/realname.tokens.inc +++ b/realname.tokens.inc @@ -40,7 +40,7 @@ function realname_tokens($type, $tokens, array $data = array(), array $options = foreach ($tokens as $name => $original) { switch ($name) { case 'name-raw': - $replacements[$original] = $sanitize ? check_plain($account->name) : $account->name; + $replacements[$original] = $sanitize ? check_plain($account->get('name')->value) : $account->get('name')->value; break; } } diff --git a/realname.views.inc b/realname.views.inc index 8dc2e95..8d23432 100644 --- a/realname.views.inc +++ b/realname.views.inc @@ -21,17 +21,17 @@ function realname_views_data() { 'title' => t('Real name'), 'help' => t("The user's real name."), 'field' => array( - 'handler' => 'views_handler_field_user', + 'id' => 'user', 'click sortable' => TRUE, ), 'sort' => array( - 'handler' => 'views_handler_sort', + 'id' => 'standard', ), 'argument' => array( - 'handler' => 'views_handler_argument_string', + 'id' => 'string', ), 'filter' => array( - 'handler' => 'views_handler_filter_string', + 'id' => 'string', 'title' => t('Name'), 'help' => t("The user's real name. This filter does not check if the user exists and allows partial matching. Does not utilize autocomplete.") ),