Commit 8e23b465 authored by alexpott's avatar alexpott

Issue #2045275 by Berdir: Convert user properties to methods.

parent 4c52a6e3
......@@ -1898,8 +1898,8 @@ function drupal_get_user_timezone() {
global $user;
$config = config('system.timezone');
if ($user && $config->get('user.configurable') && $user->id() && $user->timezone) {
return $user->timezone;
if ($user && $config->get('user.configurable') && $user->isAuthenticated() && $user->getTimezone()) {
return $user->getTimezone();
}
else {
// Ignore PHP strict notice if time zone has not yet been set in the php.ini
......
......@@ -4181,7 +4181,7 @@ function drupal_render_cid_parts($granularity = NULL) {
// resource drag for sites with many users, so when a module is being
// equivocal, we favor the less expensive 'PER_ROLE' pattern.
if ($granularity & DRUPAL_CACHE_PER_ROLE) {
$cid_parts[] = 'r.' . implode(',', $user->roles);
$cid_parts[] = 'r.' . implode(',', $user->getRoles());
}
elseif ($granularity & DRUPAL_CACHE_PER_USER) {
$cid_parts[] = 'u.' . $user->id();
......
......@@ -2537,8 +2537,8 @@ function install_configure_form_submit($form, &$form_state) {
// We precreated user 1 with placeholder values. Let's save the real values.
$account = user_load(1);
$account->init = $account->mail = $form_state['values']['account']['mail'];
$account->roles = !empty($account->roles) ? $account->roles : array();
$account->status = 1;
$account->roles = $account->getRoles();
$account->activate();
$account->timezone = $form_state['values']['date_default_timezone'];
$account->pass = $form_state['values']['account']['pass'];
$account->name = $form_state['values']['account']['name'];
......
......@@ -84,7 +84,7 @@ function _drupal_session_read($sid) {
// cookies (eg. web crawlers).
$insecure_session_name = substr(session_name(), 1);
if (!isset($_COOKIE[session_name()]) && !isset($_COOKIE[$insecure_session_name])) {
$user = drupal_anonymous_user();
$user = new UserSession();
return '';
}
......@@ -106,30 +106,25 @@ function _drupal_session_read($sid) {
$values = db_query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = :sid", array(':sid' => $sid))->fetchAssoc();
}
if ($values) {
$user = new UserSession($values);
}
// We found the client's session record and they are an authenticated,
// active user.
if ($values && $values['uid'] > 0 && $values['status'] == 1) {
$user = new UserSession($values);
// Add roles element to $user.
$rids = db_query("SELECT ur.rid FROM {users_roles} ur WHERE ur.uid = :uid", array(':uid' => $user->id()))->fetchCol();
$user->roles = array_merge(array(DRUPAL_AUTHENTICATED_RID), $rids);
$rids = db_query("SELECT ur.rid FROM {users_roles} ur WHERE ur.uid = :uid", array(':uid' => $values['uid']))->fetchCol();
$values['roles'] = array_merge(array(DRUPAL_AUTHENTICATED_RID), $rids);
$user = new UserSession($values);
}
elseif ($user) {
elseif ($values) {
// The user is anonymous or blocked. Only preserve two fields from the
// {sessions} table.
$account = drupal_anonymous_user();
$account->session = $user->session;
$account->timestamp = $user->timestamp;
$user = $account;
$user = new UserSession(array(
'session' => $values['session'],
'access' => $values['access'],
));
}
else {
// The session has expired.
$user = drupal_anonymous_user();
$user->session = '';
$user = new UserSession();
}
// Store the session that was read for comparison in _drupal_session_write().
......@@ -177,7 +172,7 @@ function _drupal_session_write($sid, $value) {
// For performance reasons, do not update the sessions table, unless
// $_SESSION has changed or more than 180 has passed since the last update.
if ($is_changed || !isset($user->timestamp) || REQUEST_TIME - $user->timestamp > settings()->get('session_write_interval', 180)) {
if ($is_changed || !$user->getLastAccessedTime() || REQUEST_TIME - $user->getLastAccessedTime() > settings()->get('session_write_interval', 180)) {
// Either ssid or sid or both will be added from $key below.
$fields = array(
'uid' => $user->id(),
......@@ -214,7 +209,7 @@ function _drupal_session_write($sid, $value) {
}
// Likewise, do not update access time more than once per 180 seconds.
if ($user->isAuthenticated() && REQUEST_TIME - $user->access > settings()->get('session_write_interval', 180)) {
if ($user->isAuthenticated() && REQUEST_TIME - $user->getLastAccessedTime() > settings()->get('session_write_interval', 180)) {
db_update('users')
->fields(array(
'access' => REQUEST_TIME
......
......@@ -7,6 +7,8 @@
namespace Drupal\Core\Password;
use Drupal\user\UserInterface;
/**
* Secure password hashing functions for user authentication.
*/
......@@ -32,13 +34,13 @@ public function hash($password);
*
* @param string $password
* A plain-text password
* @param Drupal\user\User
* A user object with at least the fields from the {users} table.
* @param \Drupal\user\UserInterface $account
* A user entity.
*
* @return bolean.
* TRUE or FALSE.
* @return bool
* TRUE if the password is valid, FALSE if not.
*/
public function check($password, $account);
public function check($password, UserInterface $account);
/**
* Check whether a user's hashed password needs to be replaced with a new hash.
......@@ -53,12 +55,12 @@ public function check($password, $account);
* Alternative implementations of this function might use other criteria based
* on the fields in $account.
*
* @param Drupal\user\User
* A user object with at least the fields from the {users} table.
* @param \Drupal\user\UserInterface $account
* A user entity.
*
* @return boolean
* TRUE or FALSE.
*/
public function userNeedsNewHash($account);
public function userNeedsNewHash(UserInterface $account);
}
......@@ -8,6 +8,7 @@
namespace Drupal\Core\Password;
use Drupal\Component\Utility\Crypt;
use Drupal\user\UserInterface;
/**
* Secure password hashing functions based on the Portable PHP password
......@@ -214,16 +215,16 @@ public function hash($password) {
/**
* Implements Drupal\Core\Password\PasswordInterface::checkPassword().
*/
public function check($password, $account) {
if (substr($account->pass, 0, 2) == 'U$') {
public function check($password, UserInterface $account) {
if (substr($account->getPassword(), 0, 2) == 'U$') {
// This may be an updated password from user_update_7000(). Such hashes
// have 'U' added as the first character and need an extra md5() (see the
// Drupal 7 documentation).
$stored_hash = substr($account->pass, 1);
$stored_hash = substr($account->getPassword(), 1);
$password = md5($password);
}
else {
$stored_hash = $account->pass;
$stored_hash = $account->getPassword();
}
$type = substr($stored_hash, 0, 3);
......@@ -248,14 +249,14 @@ public function check($password, $account) {
/**
* Implements Drupal\Core\Password\PasswordInterface::userNeedsNewHash().
*/
public function userNeedsNewHash($account) {
public function userNeedsNewHash(UserInterface $account) {
// Check whether this was an updated password.
if ((substr($account->pass, 0, 3) != '$S$') || (strlen($account->pass) != static::HASH_LENGTH)) {
if ((substr($account->getPassword(), 0, 3) != '$S$') || (strlen($account->getPassword()) != static::HASH_LENGTH)) {
return TRUE;
}
// Ensure that $count_log2 is within set bounds.
$count_log2 = $this->enforceLog2Boundaries($this->countLog2);
// Check whether the iteration count used differs from the standard number.
return ($this->getCountLog2($account->pass) !== $count_log2);
return ($this->getCountLog2($account->getPassword()) !== $count_log2);
}
}
......@@ -125,4 +125,30 @@ public function getPreferredAdminLangcode($default = NULL);
*/
public function getUsername();
/**
* Returns the e-mail address of this account.
*
* @return string
* The e-mail address.
*/
public function getEmail();
/**
* Returns the timezone of this account.
*
* @return string
* Name of the timezone.
*/
public function getTimeZone();
/**
* The timestamp when the account last accessed the site.
*
* A value of 0 means the user has never accessed the site.
*
* @return int
* Timestamp of the last access.
*/
public function getLastAccessedTime();
}
......@@ -19,23 +19,16 @@ class UserSession implements AccountInterface {
*
* @var int
*/
protected $uid;
/**
* Session hostname.
*
* @todo: This does not seem to be used, remove?
*
* @var string
*/
public $hostname;
protected $uid = 0;
/**
* List of the roles this user has.
*
* Defaults to the anonymous role.
*
* @var array
*/
public $roles;
protected $roles = array('anonymous');
/**
* Session ID.
......@@ -63,7 +56,7 @@ class UserSession implements AccountInterface {
*
* @var string.
*/
public $timestamp;
protected $timestamp;
/**
* The name of this account.
......@@ -86,6 +79,20 @@ class UserSession implements AccountInterface {
*/
protected $preferred_admin_langcode;
/**
* The e-mail address of this account.
*
* @var string
*/
protected $mail;
/**
* The timezone of this account.
*
* @var string
*/
protected $timezone;
/**
* Constructs a new user session.
*
......@@ -202,4 +209,25 @@ public function getUsername() {
return $name;
}
/**
* {@inheritdoc}
*/
public function getEmail() {
return $this->mail;
}
/**
* {@inheritdoc}
*/
public function getTimeZone() {
return $this->timezone;
}
/**
* {@inheritdoc}
*/
public function getLastAccessedTime() {
return $this->timestamp;
}
}
......@@ -42,7 +42,7 @@ protected function checkAccess(EntityInterface $entity, $operation, $langcode, A
// For blocks with roles associated, if none of the user's roles matches
// the settings from this block, access is denied.
$visibility = $entity->get('visibility');
if (!empty($visibility['role']['roles']) && !array_intersect(array_filter($visibility['role']['roles']), $user->roles)) {
if (!empty($visibility['role']['roles']) && !array_intersect(array_filter($visibility['role']['roles']), $user->getRoles())) {
// No match.
return FALSE;
}
......
......@@ -46,7 +46,7 @@ function setUp() {
$this->normal_user_alt = $this->drupalCreateUser();
// Sync the roles, since drupalCreateUser() creates separate roles for
// the same permission sets.
$this->normal_user_alt->roles = $this->normal_user->roles;
$this->normal_user_alt->roles = $this->normal_user->getRoles();
$this->normal_user_alt->save();
// Enable our test block.
......
......@@ -1429,7 +1429,7 @@ function comment_preview(Comment $comment) {
if ($account->id()) {
$comment->uid->target_id = $account->id();
$comment->name->value = check_plain($account->name);
$comment->name->value = check_plain($account->getUsername());
}
elseif (empty($comment->name->value)) {
$comment->name->value = config('user.settings')->get('anonymous');
......@@ -1530,8 +1530,8 @@ function template_preprocess_comment(&$variables) {
$variables['user_picture'] = array();
}
if (config('user.settings')->get('signatures') && !empty($account->signature)) {
$variables['signature'] = check_markup($account->signature, $account->signature_format, '', TRUE) ;
if (config('user.settings')->get('signatures') && $account->getSignature()) {
$variables['signature'] = check_markup($account->getSignature(), $account->getSignatureFormat(), '', TRUE) ;
}
else {
$variables['signature'] = '';
......
......@@ -139,8 +139,7 @@ function comment_tokens($type, $tokens, array $data = array(), array $options =
case 'mail':
if ($comment->uid->target_id != 0) {
$account = user_load($comment->uid->target_id);
$mail = $account->mail;
$mail = $comment->uid->entity->getEmail();
}
else {
$mail = $comment->mail->value;
......
......@@ -66,7 +66,7 @@ public function form(array $form, array &$form_state) {
}
else {
if ($user->isAuthenticated()) {
$author = $user->name;
$author = $user->getUsername();
}
else {
$author = ($comment->name->value ? $comment->name->value : '');
......
......@@ -302,7 +302,7 @@ public function preSave(EntityStorageControllerInterface $storage_controller) {
// We test the value with '===' because we need to modify anonymous
// users as well.
if ($this->uid->target_id === $user->id() && $user->isAuthenticated()) {
$this->name->value = $user->name;
$this->name->value = $user->getUsername();
}
// Add the values which aren't passed into the function.
$this->thread->value = $thread;
......
......@@ -70,7 +70,7 @@ function testAnonymous() {
// Ensure anonymous users cannot post in the name of registered users.
$langcode = Language::LANGCODE_NOT_SPECIFIED;
$edit = array(
'name' => $this->admin_user->name,
'name' => $this->admin_user->getUsername(),
'mail' => $this->randomName() . '@example.com',
'subject' => $this->randomName(),
"comment_body[$langcode][0][value]" => $this->randomName(),
......
......@@ -80,8 +80,8 @@ function testCommentInterface() {
// Test changing the comment author to a verified user.
$this->drupalGet('comment/' . $comment->id() . '/edit');
$comment = $this->postComment(NULL, $comment->comment_body->value, $comment->subject->value, array('name' => $this->web_user->name));
$this->assertTrue($comment->name->value == $this->web_user->name && $comment->uid->target_id == $this->web_user->id(), 'Comment author successfully changed to a registered user.');
$comment = $this->postComment(NULL, $comment->comment_body->value, $comment->subject->value, array('name' => $this->web_user->getUsername()));
$this->assertTrue($comment->name->value == $this->web_user->getUsername() && $comment->uid->target_id == $this->web_user->id(), 'Comment author successfully changed to a registered user.');
$this->drupalLogout();
......
......@@ -52,7 +52,7 @@ function testCommentLinks() {
// Remove additional user permissions from $this->web_user added by setUp(),
// since this test is limited to anonymous and authenticated roles only.
$roles = $this->web_user->roles;
$roles = $this->web_user->getRoles();
entity_delete_multiple('user_role', array(reset($roles)));
// Matrix of possible environmental conditions and configuration settings.
......
......@@ -92,7 +92,7 @@ function testCommentEditPreviewSave() {
$date = new DrupalDateTime('2008-03-02 17:23');
$edit['subject'] = $this->randomName(8);
$edit['comment_body[' . $langcode . '][0][value]'] = $this->randomName(16);
$edit['name'] = $web_user->name;
$edit['name'] = $web_user->getUsername();
$edit['date[date]'] = $date->format('Y-m-d');
$edit['date[time]'] = $date->format('H:i:s');
$raw_date = $date->getTimestamp();
......
......@@ -56,7 +56,7 @@ function testCommentTokenReplacement() {
$tests['[comment:cid]'] = $comment->id();
$tests['[comment:hostname]'] = check_plain($comment->hostname->value);
$tests['[comment:name]'] = filter_xss($comment->name->value);
$tests['[comment:mail]'] = check_plain($this->admin_user->mail);
$tests['[comment:mail]'] = check_plain($this->admin_user->getEmail());
$tests['[comment:homepage]'] = check_url($comment->homepage->value);
$tests['[comment:title]'] = filter_xss($comment->subject->value);
$tests['[comment:body]'] = $comment->comment_body->processed;
......@@ -69,7 +69,7 @@ function testCommentTokenReplacement() {
$tests['[comment:node:nid]'] = $comment->nid->target_id;
$tests['[comment:node:title]'] = check_plain($node->title);
$tests['[comment:author:uid]'] = $comment->uid->target_id;
$tests['[comment:author:name]'] = check_plain($this->admin_user->name);
$tests['[comment:author:name]'] = check_plain($this->admin_user->getUsername());
// Test to make sure that we generated something for each token.
$this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
......@@ -82,13 +82,13 @@ function testCommentTokenReplacement() {
// Generate and test unsanitized tokens.
$tests['[comment:hostname]'] = $comment->hostname->value;
$tests['[comment:name]'] = $comment->name->value;
$tests['[comment:mail]'] = $this->admin_user->mail;
$tests['[comment:mail]'] = $this->admin_user->getEmail();
$tests['[comment:homepage]'] = $comment->homepage->value;
$tests['[comment:title]'] = $comment->subject->value;
$tests['[comment:body]'] = $comment->comment_body->value;
$tests['[comment:parent:title]'] = $parent_comment->subject->value;
$tests['[comment:node:title]'] = $node->title;
$tests['[comment:author:name]'] = $this->admin_user->name;
$tests['[comment:author:name]'] = $this->admin_user->getUsername();
foreach ($tests as $input => $expected) {
$output = $token_service->replace($input, array('comment' => $comment), array('langcode' => $language_interface->id, 'sanitize' => FALSE));
......
......@@ -6,6 +6,7 @@
*/
use Drupal\contact\Plugin\Core\Entity\Category;
use Drupal\user\UserInterface;
/**
* Implements hook_help().
......@@ -122,7 +123,7 @@ function contact_menu() {
*
* @see contact_menu()
*/
function _contact_personal_tab_access($account) {
function _contact_personal_tab_access(UserInterface $account) {
global $user;
// Anonymous users cannot have contact forms.
......@@ -141,7 +142,7 @@ function _contact_personal_tab_access($account) {
}
// If requested user has been blocked, do not allow users to contact them.
if (empty($account->status)) {
if ($account->isBlocked()) {
return FALSE;
}
......@@ -274,7 +275,7 @@ function contact_mail($key, &$message, $params) {
$variables['!sender-url'] = url($sender_uri['path'], array('absolute' => TRUE, 'language' => $language) + $sender_uri['options']);
}
else {
$variables['!sender-url'] = $params['sender']->mail;
$variables['!sender-url'] = $params['sender']->getEmail();
}
$options = array('langcode' => $language->id);
......
......@@ -52,14 +52,14 @@ public function form(array $form, array &$form_state) {
// prevent the impersonation of other users.
else {
$form['name']['#type'] = 'item';
$form['name']['#value'] = $user->name;
$form['name']['#value'] = $user->getUsername();
$form['name']['#required'] = FALSE;
$form['name']['#markup'] = check_plain(user_format_name($user));
$form['name']['#markup'] = check_plain($user->getUsername());
$form['mail']['#type'] = 'item';
$form['mail']['#value'] = $user->mail;
$form['mail']['#value'] = $user->getEmail();
$form['mail']['#required'] = FALSE;
$form['mail']['#markup'] = check_plain($user->mail);
$form['mail']['#markup'] = check_plain($user->getEmail());
}
// The user contact form has a preset recipient.
......@@ -163,7 +163,7 @@ public function save(array $form, array &$form_state) {
}
elseif ($recipient = $message->getPersonalRecipient()) {
// Send to the user in the user's preferred language.
$to = $recipient->mail->value;
$to = $recipient->getEmail();
$recipient_langcode = $recipient->getPreferredLangcode();
$params['recipient'] = $recipient->getBCEntity();
}
......@@ -173,32 +173,32 @@ public function save(array $form, array &$form_state) {
// Send e-mail to the recipient(s).
$key_prefix = $message->isPersonal() ? 'user' : 'page';
drupal_mail('contact', $key_prefix . '_mail', $to, $recipient_langcode, $params, $sender->mail);
drupal_mail('contact', $key_prefix . '_mail', $to, $recipient_langcode, $params, $sender->getEmail());
// If requested, send a copy to the user, using the current language.
if ($message->copySender()) {
drupal_mail('contact', $key_prefix . '_copy', $sender->mail, $language_interface->id, $params, $sender->mail);
drupal_mail('contact', $key_prefix . '_copy', $sender->getEmail(), $language_interface->id, $params, $sender->getEmail());
}
// If configured, send an auto-reply, using the current language.
if (!$message->isPersonal() && $category->reply) {
// User contact forms do not support an auto-reply message, so this
// message always originates from the site.
drupal_mail('contact', 'page_autoreply', $sender->mail, $language_interface->id, $params);
drupal_mail('contact', 'page_autoreply', $sender->getEmail(), $language_interface->id, $params);
}
\Drupal::service('flood')->register('contact', config('contact.settings')->get('flood.interval'));
if (!$message->isPersonal()) {
watchdog('contact', '%sender-name (@sender-from) sent an e-mail regarding %category.', array(
'%sender-name' => $sender->name,
'@sender-from' => $sender->mail,
'@sender-from' => $sender->getEmail(),
'%category' => $category->label(),
));
}
else {
watchdog('contact', '%sender-name (@sender-from) sent %recipient-name an e-mail.', array(
'%sender-name' => $sender->name,
'@sender-from' => $sender->mail,
'@sender-from' => $sender->getEmail(),
'%recipient-name' => $message->recipient->name,
));
}
......
......@@ -72,17 +72,17 @@ function testSendPersonalContactMessage() {
$mails = $this->drupalGetMails();
$this->assertEqual(1, count($mails));
$mail = $mails[0];
$this->assertEqual($mail['to'], $this->contact_user->mail);
$this->assertEqual($mail['from'], $this->web_user->mail);
$this->assertEqual($mail['to'], $this->contact_user->getEmail());
$this->assertEqual($mail['from'], $this->web_user->getEmail());
$this->assertEqual($mail['key'], 'user_mail');
$variables = array(
'!site-name' => config('system.site')->get('name'),
'!subject' => $message['subject'],
'!recipient-name' => $this->contact_user->name,
'!recipient-name' => $this->contact_user->getUsername(),
);
$this->assertEqual($mail['subject'], t('[!site-name] !subject', $variables), 'Subject is in sent message.');
$this->assertTrue(strpos($mail['body'], t('Hello !recipient-name,', $variables)) !== FALSE, 'Recipient name is in sent message.');
$this->assertTrue(strpos($mail['body'], $this->web_user->name) !== FALSE, 'Sender name is in sent message.');
$this->assertTrue(strpos($mail['body'], $this->web_user->getUsername()) !== FALSE, 'Sender name is in sent message.');
$this->assertTrue(strpos($mail['body'], $message['message']) !== FALSE, 'Message body is in sent message.');
}
......@@ -154,7 +154,7 @@ function testPersonalContactAccess() {
// Re-create our contacted user as a blocked user.
$this->contact_user = $this->drupalCreateUser();
$this->contact_user->status = 0;
$this->contact_user->block();
$this->contact_user->save();
// Test that blocked users can still be contacted by admin.
......
......@@ -269,7 +269,7 @@ public function entityFormAlter(array &$form, array &$form_state, EntityInterfac
);
}
$name = $new_translation ? $GLOBALS['user']->name : user_load($entity->translation[$form_langcode]['uid'])->name;
$name = $new_translation ? $GLOBALS['user']->getUsername() : user_load($entity->translation[$form_langcode]['uid'])->getUsername();
$form['content_translation']['name'] = array(
'#type' => 'textfield',
'#title' => t('Authored by'),
......
......@@ -169,7 +169,7 @@ protected function assertAuthoringInfo() {
'created' => REQUEST_TIME - mt_rand(0, 1000),
);
$edit = array(
'content_translation[name]' => $user->name,
'content_translation[name]' => $user->getUsername(),
'content_translation[created]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d H:i:s O'),
);
$prefix = $index > 0 ? $langcode . '/' : '';
......
......@@ -118,7 +118,7 @@ function testWorkflows() {
protected function assertWorkflows(UserInterface $user, $expected_status) {
$default_langcode = $this->langcodes[0];
$languages = language_list();
$args = array('@user_label' => $user->name);
$args = array('@user_label' => $user->getUsername());
$this->drupalLogin($user);
// Check whether the user is allowed to access the entity form in edit mode.
......
......@@ -243,7 +243,7 @@ private function doUser() {
$ids[] = $row->wid;
}
$count_before = (isset($ids)) ? count($ids) : 0;
$this->assertTrue($count_before > 0, format_string('DBLog contains @count records for @name', array('@count' => $count_before, '@name' => $user->name)));
$this->assertTrue($count_before > 0, format_string('DBLog contains @count records for @name', array('@count' => $count_before, '@name' => $user->getUsername())));
// Login the admin user.
$this->drupalLogin($this->big_user);
......@@ -259,13 +259,13 @@ private function doUser() {
// Add user.
// Default display includes name and email address; if too long, the email
// address is replaced by three periods.
$this->assertLogMessage(t('New user: %name %email.', array('%name' => $name, '%email' => '<' . $user->mail . '>')), 'DBLog event was recorded: [add user]');
$this->assertLogMessage(t('New user: %name %email.', array('%name' => $name, '%email' => '<' . $user->getEmail() . '>')), 'DBLog event was recorded: [add user]');
// Login user.
$this->assertLogMessage(t('Session opened for %name.', array('%name' => $name)), 'DBLog event was recorded: [login user]');
// Logout user.
$this->assertLogMessage(t('Session closed for %name.', array('%name' => $name)), 'DBLog event was recorded: [logout user]');
// Delete user.
$message = t('Deleted user: %name %email.', array('%name' => $name, '%email' => '<' . $user->mail . '>'));
$message = t('Deleted user: %name %email.', array('%name' => $name, '%email' => '<' . $user->getEmail() . '>'));