get folder ACL and vacation mostly working for admin usage

This commit is contained in:
Ralf Becker 2014-09-24 17:25:18 +00:00
parent f0bb5a8957
commit 3ed259f85f
5 changed files with 92 additions and 61 deletions

View File

@ -43,24 +43,19 @@ class mail_acl
*/ */
var $mail_bo; var $mail_bo;
/**
* imap object instanciated in constructor for account to edit
*
* @var emailadmin_imap
*/
var $imap;
/** /**
* *
* @var mail_account * @var mail_account
*/ */
var $current_account; var $current_account;
/**
* Constructor
*
*
*/
function __construct()
{
$acc_id = $_GET['acc_id']?$_GET['acc_id']:$GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'];
$this->mail_bo = mail_bo::getInstance(false, $acc_id);
}
/** /**
* Edit folder ACLs of account(s) * Edit folder ACLs of account(s)
* *
@ -70,12 +65,33 @@ class mail_acl
*/ */
function edit(array $content=null ,$msg='') function edit(array $content=null ,$msg='')
{ {
if (!is_array($content))
{
$acc_id = $_GET['acc_id']?$_GET['acc_id']:$GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'];
if (isset($_GET['account_id']) && !isset($GLOBALS['egw_info']['user']['apps']['admin']))
{
egw_framework::window_close(lang('Permission denied'));
}
$account_id = $_GET['account_id'];
}
else
{
$acc_id = $content['acc_id'];
$account_id = $content['account_id'];
}
$account = emailadmin_account::read($acc_id, $account_id);
$this->imap = $account->imapServer(isset($account_id) ? (int)$account_id : false);
$tmpl = new etemplate_new('mail.acl'); $tmpl = new etemplate_new('mail.acl');
$mailbox = $_GET['mailbox']? base64_decode($_GET['mailbox']): $content['mailbox'][0]; $mailbox = $_GET['mailbox']? base64_decode($_GET['mailbox']): $content['mailbox'][0];
if (empty($mailbox))
{
$mailbox = $this->imap->isAdminConnection ? $this->imap->getUserMailboxString($this->imap->isAdminConnection) : 'INBOX';
}
// Unset the content if folder is changed, in order to read acl rights for new selected folder // Unset the content if folder is changed, in order to read acl rights for new selected folder
if (!is_array($content['button']) && is_array($content['mailbox']) && !is_array($content['grid']['delete'])) unset($content); if (!is_array($content['button']) && is_array($content['mailbox']) && !is_array($content['grid']['delete'])) unset($content);
if (!is_array($content)) if (!is_array($content))
{ {
if (!empty($mailbox)) if (!empty($mailbox))
@ -107,9 +123,9 @@ class mail_acl
{ {
$content['grid'][$n]['acl'] = 'custom'; $content['grid'][$n]['acl'] = 'custom';
} }
if (($account_id = $this->mail_bo->icServer->getMailBoxAccountId($key))) if (($user = $this->imap->getMailBoxAccountId($key)))
{ {
$content['grid'][$n++]['acc_id'] = $account_id; $content['grid'][$n++]['acc_id'] = $user;
} }
else else
{ {
@ -146,7 +162,7 @@ class mail_acl
$tmpl->set_validation_error('grid['.$row.']'.'[acc_id]', "You must fill this field!"); $tmpl->set_validation_error('grid['.$row.']'.'[acc_id]', "You must fill this field!");
} }
} }
//Add new row at the end //Add new row at the end
if ($content['grid'][count($content['grid'])]['acc_id']) if ($content['grid'][count($content['grid'])]['acc_id'])
array_push($content['grid'], array('acc_id'=>'')); array_push($content['grid'], array('acc_id'=>''));
@ -183,8 +199,8 @@ class mail_acl
//Make the account owner's fields all readonly as owner has all rights and should not be able to change them //Make the account owner's fields all readonly as owner has all rights and should not be able to change them
foreach($content['grid'] as $key => $fields) foreach($content['grid'] as $key => $fields)
{ {
if ($fields['acc_id'] == $this->mail_bo->icServer->acc_imap_username || if ($fields['acc_id'] == $this->imap->acc_imap_username ||
$fields['acc_id'][0] == $this->mail_bo->icServer->acc_imap_username) $fields['acc_id'][0] == $this->imap->acc_imap_username)
{ {
foreach ($fields as $index => $val) foreach ($fields as $index => $val)
{ {
@ -198,10 +214,12 @@ class mail_acl
} }
//Make entry row's delete button readonly //Make entry row's delete button readonly
$readonlys['grid']['delete['.count($content['grid']).']'] = true; $readonlys['grid']['delete['.count($content['grid']).']'] = true;
$preserv ['mailbox'] = $content['mailbox']; $preserv['mailbox'] = $content['mailbox'];
$content['msg'] = $msg; $preserv['acc_id'] = $acc_id;
$content['grid']['account_type'] = $this->mail_bo->icServer->supportsGroupAcl() ? 'both' : 'accounts'; $preserv['account_id'] = $account_id;
$content['grid']['account_type'] = $this->imap->supportsGroupAcl() ? 'both' : 'accounts';
$tmpl->exec('mail.mail_acl.edit', $content, $sel_options, $readonlys, $preserv,2); $tmpl->exec('mail.mail_acl.edit', $content, $sel_options, $readonlys, $preserv,2);
} }
@ -217,7 +235,7 @@ class mail_acl
function update_acl ($content, &$msg) function update_acl ($content, &$msg)
{ {
$validator = array(); $validator = array();
foreach ($content['grid'] as $keys => $value) foreach ($content['grid'] as $keys => $value)
{ {
$recursive = $value['acl_recursive']; $recursive = $value['acl_recursive'];
@ -236,10 +254,10 @@ class mail_acl
$options['rights'] .= $right[1]; $options['rights'] .= $right[1];
} }
} }
$username = $content['grid'][$keys]['acc_id'] == $this->mail_bo->icServer->acc_imap_username $username = $content['grid'][$keys]['acc_id'] == $this->imap->acc_imap_username
?$content['grid'][$keys]['acc_id']:$content['grid'][$keys]['acc_id'][0]; ?$content['grid'][$keys]['acc_id']:$content['grid'][$keys]['acc_id'][0];
//error_log(__METHOD__."(".__LINE__.") setACL($content[mailbox], $username, ".array2string($options).", $recursive)"); //error_log(__METHOD__."(".__LINE__.") setACL($content[mailbox], $username, ".array2string($options).", $recursive)");
if (is_numeric($username) && ($u = $this->mail_bo->icServer->getMailBoxUserName($username))) if (is_numeric($username) && ($u = $this->imap->getMailBoxUserName($username)))
{ {
$username = $u; $username = $u;
} }
@ -248,12 +266,12 @@ class mail_acl
//error_log(__METHOD__."() setACL($content[mailbox], $username, ".array2string($options).", $recursive)"); //error_log(__METHOD__."() setACL($content[mailbox], $username, ".array2string($options).", $recursive)");
if (($ret=$this->setACL($content['mailbox'], $username, $options, $recursive, $msg))) if (($ret=$this->setACL($content['mailbox'], $username, $options, $recursive, $msg)))
{ {
$msg = lang("The Folder %1 's ACLs saved!", $content['mailbox']); $msg = lang("The Folder %1 's ACLs saved", $content['mailbox']);
} }
else else
{ {
$msg = lang('Error while setting folder '.$content['mailbox']. $msg); $msg = lang('Error while setting ACL for folder %1!', $content['mailbox']).' '.$msg;
} }
} }
else else
@ -268,7 +286,7 @@ class mail_acl
if (is_array($validator)) if (is_array($validator))
{ {
return $validator; return $validator;
} }
} }
/** /**
@ -279,7 +297,7 @@ class mail_acl
{ {
if (($acl = $this->getACL($mailbox))) if (($acl = $this->getACL($mailbox)))
{ {
$msg = lang('ACL rights retrived successfully!'); $msg = lang('ACL rights retrived successfully');
return $acl; return $acl;
} }
else else
@ -304,7 +322,8 @@ class mail_acl
if ($row_num) $row_num = $row_num[0]; if ($row_num) $row_num = $row_num[0];
$recursive = $content['grid'][$row_num]['acl_recursive']; $recursive = $content['grid'][$row_num]['acl_recursive'];
$identifier = $content['grid'][$row_num]['acc_id'][0]; $identifier = $content['grid'][$row_num]['acc_id'][0];
if (is_numeric($identifier) && ($u = $this->mail_bo->icServer->getMailBoxUserName($identifier))) if (is_array($content['mailbox'])) $content['mailbox'] = $content['mailbox'][0];
if (is_numeric($identifier) && ($u = $this->imap->getMailBoxUserName($identifier)))
{ {
$identifier = $u; $identifier = $u;
} }
@ -315,18 +334,18 @@ class mail_acl
unset($content['grid']['delete']); unset($content['grid']['delete']);
if ($recursive) if ($recursive)
{ {
$msg = lang("The %1 's acl, including its subfolders, removed from the %2!",$content['mailbox'],$identifier); $msg = lang("The %1 's acl, including its subfolders, removed from the %2",$content['mailbox'],$identifier);
} }
else else
{ {
$msg = lang("The %1 's acl removed from the %2!",$content['mailbox'],$identifier); $msg = lang("The %1 's acl removed from the %2",$content['mailbox'],$identifier);
} }
return array_combine(range(1, count($content['grid'])), array_values($content['grid'])); return array_combine(range(1, count($content['grid'])), array_values($content['grid']));
} }
else else
{ {
$msg = lang("An error happend while trying to remove ACL rights from the account %1.",$identifier); $msg = lang("An error happend while trying to remove ACL rights from the account %1!",$identifier);
return false; return false;
} }
} }
@ -355,7 +374,7 @@ class mail_acl
{ {
try try
{ {
$this->mail_bo->icServer->deleteACL($sbFolders, $identifier); $this->imap->deleteACL($sbFolders, $identifier);
} }
catch (Exception $e) catch (Exception $e)
{ {
@ -376,14 +395,14 @@ class mail_acl
*/ */
function getSubfolders($mailbox) function getSubfolders($mailbox)
{ {
$delimiter = $this->mail_bo->getHierarchyDelimiter(); $delimiter = $this->imap->getDelimiter();
$nameSpace = $this->mail_bo->_getNameSpaces(); /* $nameSpace = $this->mail_bo->_getNameSpaces();
$prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $mailbox); $prefix = $this->mail_bo->getFolderPrefixFromNamespace($nameSpace, $mailbox);
if (($subFolders = $this->mail_bo->getMailBoxesRecursive($mailbox, $delimiter, $prefix))) if (($subFolders = $this->mail_bo->getMailBoxesRecursive($mailbox, $delimiter, $prefix)))
{ {
return $subFolders; return $subFolders;
} }
else else*/
{ {
return array(); return array();
} }
@ -417,7 +436,7 @@ class mail_acl
{ {
try try
{ {
$this->mail_bo->icServer->setACL($sbFolders,$identifier,$options); $this->imap->setACL($sbFolders,$identifier,$options);
} }
catch (Exception $e) catch (Exception $e)
{ {
@ -439,7 +458,7 @@ class mail_acl
{ {
try try
{ {
$acl = $this->mail_bo->icServer->getACL($mailbox); $acl = $this->imap->getACL($mailbox);
return $acl; return $acl;
} catch (Exception $e) { } catch (Exception $e) {
error_log(__METHOD__. "Could not get ACL rights from folder " . $mailbox . " because of " .$e->getMessage()); error_log(__METHOD__. "Could not get ACL rights from folder " . $mailbox . " because of " .$e->getMessage());

View File

@ -37,11 +37,11 @@ class mail_hooks
'caption' => 'Folder ACL', 'caption' => 'Folder ACL',
'icon' => 'lock', 'icon' => 'lock',
'popup' => '750x420', 'popup' => '750x420',
'url' => array( 'url' => egw::link('/index.php', array(
'menuaction' => 'mail.mail_acl.edit', 'menuaction' => 'mail.mail_acl.edit',
'acc_id' => $data['acc_id'], 'acc_id' => $data['acc_id'],
'account_id' => $data['account_id'], 'account_id' => $data['account_id'],
), )),
); );
} }
if ($account['acc_sieve_enabled'] || $account['acc_imap_type'] == 'managementserver_imap') if ($account['acc_sieve_enabled'] || $account['acc_imap_type'] == 'managementserver_imap')
@ -51,11 +51,11 @@ class mail_hooks
'caption' => 'Vacation notice', 'caption' => 'Vacation notice',
'icon' => 'mail/navbar', 'icon' => 'mail/navbar',
'popup' => '750x420', 'popup' => '750x420',
'url' => array( 'url' => egw::link('/index.php', array(
'menuaction' => 'mail.mail_sieve.editVacation', 'menuaction' => 'mail.mail_sieve.editVacation',
'acc_id' => $data['acc_id'], 'acc_id' => $data['acc_id'],
'account_id' => $data['account_id'], 'account_id' => $data['account_id'],
), )),
); );
} }
} }

View File

@ -481,17 +481,27 @@ class mail_sieve
{ {
foreach(emailadmin_account::search($account_id, false, null, false, 0, false) as $account) foreach(emailadmin_account::search($account_id, false, null, false, 0, false) as $account)
{ {
// check if account is valid for multiple users, has admin credentials and sieve enabled try {
if (emailadmin_account::is_multiple($account) && // check if account is valid for multiple users, has admin credentials and sieve enabled
($icServer = $account->imapServer(true)) && // check on icServer object, so plugins can overwrite if (emailadmin_account::is_multiple($account) &&
$icServer->acc_imap_admin_username && $icServer->acc_sieve_enabled) ($icServer = $account->imapServer(true)) && // check on icServer object, so plugins can overwrite
{ $icServer->acc_imap_admin_username && $icServer->acc_sieve_enabled)
$allAccounts[$account->acc_id] = $account->acc_name; {
$accounts[$account->acc_id] = $account; $allAccounts[$account->acc_id] = $account->acc_name;
$accounts[$account->acc_id] = $account;
}
}
catch(Exception $e) {
unset($e);
// ignore broken accounts
} }
} }
$profileID = !isset($content['acc_id']) ? key($accounts):$content['acc_id']; $profileID = !isset($content['acc_id']) ? key($accounts):$content['acc_id'];
if (isset($_GET['acc_id']) && isset($allAccounts[$_GET['acc_id']]))
{
$profileID = $content['acc_id'] = (int)$_GET['acc_id'];
}
//Chooses the right account //Chooses the right account
$this->account = $accounts[$profileID]; $this->account = $accounts[$profileID];
@ -624,7 +634,7 @@ class mail_sieve
$cachedVacations = egw_cache::getCache(egw_cache::INSTANCE, 'email', 'vacationNotice'+$GLOBALS['egw_info']['user']['account_lid']); $cachedVacations = egw_cache::getCache(egw_cache::INSTANCE, 'email', 'vacationNotice'+$GLOBALS['egw_info']['user']['account_lid']);
$cachedVacations = array($icServer->acc_id => $newVacation) + (array)$cachedVacations; $cachedVacations = array($icServer->acc_id => $newVacation) + (array)$cachedVacations;
egw_cache::setCache(egw_cache::INSTANCE,'email', 'vacationNotice'+$GLOBALS['egw_info']['user']['account_lid'], $cachedVacations); egw_cache::setCache(egw_cache::INSTANCE,'email', 'vacationNotice'+$GLOBALS['egw_info']['user']['account_lid'], $cachedVacations);
$msg = lang('Vacation notice sucessfully updated.'); $msg = lang('Vacation notice sucessfully updated.');
} }
} }

View File

@ -30,7 +30,7 @@ allow images from external sources in html emails mail de Erlaube Bilder von ext
allow users to create further identities mail de Anwender dürfen weitere Identitäten hinzufügen allow users to create further identities mail de Anwender dürfen weitere Identitäten hinzufügen
allways a new window mail de immer in einem neuen Fenster allways a new window mail de immer in einem neuen Fenster
always show html emails mail de HTML-E-Mails immer anzeigen always show html emails mail de HTML-E-Mails immer anzeigen
an error happend while trying to remove acl rights from the account %1. mail de Ein Fehler trat auf beim Versuch die Zugriffskontrollrechte vom Konto %1 zu entfernen. an error happend while trying to remove acl rights from the account %1! mail de Ein Fehler trat auf beim Versuch die Zugriffskontrollrechte vom Konto %1 zu entfernen!
and the rule with priority %1, now got the priority %2 mail de Die Regel mit Priorität %1 hat jetzt die Priorität %2 and the rule with priority %1, now got the priority %2 mail de Die Regel mit Priorität %1 hat jetzt die Priorität %2
any of mail de mit einem any of mail de mit einem
any status mail de Alle Status any status mail de Alle Status
@ -97,6 +97,7 @@ do not auto create folders mail de Automatische Ordnererstellung verhindern für
do you really want to apply %1 to all messages in the current view? mail de Wollen Sie wirklich %1 auf alle Nachrichten in der aktuellen Ansicht anwenden? do you really want to apply %1 to all messages in the current view? mail de Wollen Sie wirklich %1 auf alle Nachrichten in der aktuellen Ansicht anwenden?
do you really want to delete folder %1 ? mail de Wollen Sie den Ordner %1 wirklich löschen? do you really want to delete folder %1 ? mail de Wollen Sie den Ordner %1 wirklich löschen?
do you really want to mark all messages as read in the current folder? mail de Wollen Sie wirklich alle eMails im aktuellen Ordner als gelesen markieren? do you really want to mark all messages as read in the current folder? mail de Wollen Sie wirklich alle eMails im aktuellen Ordner als gelesen markieren?
do you really want to remove all rights from this account mail de Wollen Sie wirklich alle Rechte von diesem Benutzer löschen
do you really want to toggle flag %1 for all messages in current view? mail de Wollen Sie wirklich den Wert für %1 für alle Nachrichten in der aktuellen Ansicht umschalten? do you really want to toggle flag %1 for all messages in current view? mail de Wollen Sie wirklich den Wert für %1 für alle Nachrichten in der aktuellen Ansicht umschalten?
do you want to be asked for confirmation before attaching selected messages to new mail? mail de Möchten Sie vor dem Anhängen von einer oder mehreren (ausgewählten) E-Mails an eine neue E-Mail gefragt werden? do you want to be asked for confirmation before attaching selected messages to new mail? mail de Möchten Sie vor dem Anhängen von einer oder mehreren (ausgewählten) E-Mails an eine neue E-Mail gefragt werden?
do you want to be asked for confirmation before moving selected messages to another folder? mail de Möchten Sie vor dem Verschieben von E-Mails in andere Ordner gefragt werden? do you want to be asked for confirmation before moving selected messages to another folder? mail de Möchten Sie vor dem Verschieben von E-Mails in andere Ordner gefragt werden?
@ -407,9 +408,9 @@ test connection and display basic information about the selected profile mail de
text mail de Text text mail de Text
text mode mail de Text Modus text mode mail de Text Modus
text/plain mail de text/plain text/plain mail de text/plain
the %1 's acl removed from the %2! mail de Die Zugriffskontrollrechte von %1 wurden von %2 entfernt! the %1 's acl removed from the %2 mail de Die Zugriffskontrollrechte von %1 wurden von %2 entfernt
the %1 's acl, including its subfolders, removed from the %2! mail de Die Zugriffskontrollrechte inkl. Unterordner von %1 wurden von %2 entfernt! the %1 's acl, including its subfolders, removed from the %2 mail de Die Zugriffskontrollrechte inkl. Unterordner von %1 wurden von %2 entfernt
the folder %1 's acls saved! mail de Die Zugriffskontrollrechte des Ordners %1 wurden gespeichert! the folder %1 's acls saved mail de Die Zugriffskontrollrechte des Ordners %1 wurden gespeichert
the folder <b>%1</b> will be used, if there is nothing set here, and no valid predefine given. mail de Der Ordner <b>%1</b> wird verwendet, wenn hier nichts gesetzt ist und kein gültiger Vorgabewert eingetragen ist. the folder <b>%1</b> will be used, if there is nothing set here, and no valid predefine given. mail de Der Ordner <b>%1</b> wird verwendet, wenn hier nichts gesetzt ist und kein gültiger Vorgabewert eingetragen ist.
the message sender has requested a response to indicate that you have read this message. would you like to send a receipt? mail de Der Absender hat ein Empfangsbestätigung angefordert, um sicherzustellen, das Sie diese E-Mail gelesen haben. Möchten Sie ein Empfangsbestätigung senden? the message sender has requested a response to indicate that you have read this message. would you like to send a receipt? mail de Der Absender hat ein Empfangsbestätigung angefordert, um sicherzustellen, das Sie diese E-Mail gelesen haben. Möchten Sie ein Empfangsbestätigung senden?
the mimeparser can not parse this message. mail de Der MIME Parser versteht diese Nachricht nicht. the mimeparser can not parse this message. mail de Der MIME Parser versteht diese Nachricht nicht.

View File

@ -30,7 +30,7 @@ allow images from external sources in html emails mail en allow images from exte
allow users to create further identities mail en allow users to create further identities allow users to create further identities mail en allow users to create further identities
allways a new window mail en allways a new window allways a new window mail en allways a new window
always show html emails mail en always show html emails always show html emails mail en always show html emails
an error happend while trying to remove acl rights from the account %1. mail en An error happend while trying to remove ACL rights from the account %1. an error happend while trying to remove acl rights from the account %1! mail en An error happend while trying to remove ACL rights from the account %1!
and the rule with priority %1, now got the priority %2 mail en And the rule with priority %1, now got the priority %2 and the rule with priority %1, now got the priority %2 mail en And the rule with priority %1, now got the priority %2
any of mail en any of any of mail en any of
any status mail en any status any status mail en any status
@ -97,6 +97,7 @@ do not auto create folders mail en do not auto create folders
do you really want to apply %1 to all messages in the current view? mail en Do you really want to apply %1 to ALL messages in the current view? do you really want to apply %1 to all messages in the current view? mail en Do you really want to apply %1 to ALL messages in the current view?
do you really want to delete folder %1 ? mail en Do you really want to DELETE Folder %1 ? do you really want to delete folder %1 ? mail en Do you really want to DELETE Folder %1 ?
do you really want to mark all messages as read in the current folder? mail en Do you really want to mark ALL messages as read in the current folder? do you really want to mark all messages as read in the current folder? mail en Do you really want to mark ALL messages as read in the current folder?
do you really want to remove all rights from this account mail en Do you really want to remove all rights from this account
do you really want to toggle flag %1 for all messages in current view? mail en Do you really want to toggle flag %1 for ALL messages in current view? do you really want to toggle flag %1 for all messages in current view? mail en Do you really want to toggle flag %1 for ALL messages in current view?
do you want to be asked for confirmation before attaching selected messages to new mail? mail en Do you want to be asked for confirmation before attaching selected messages to new mail? do you want to be asked for confirmation before attaching selected messages to new mail? mail en Do you want to be asked for confirmation before attaching selected messages to new mail?
do you want to be asked for confirmation before moving selected messages to another folder? mail en Do you want to be asked for confirmation before moving selected messages to another folder? do you want to be asked for confirmation before moving selected messages to another folder? mail en Do you want to be asked for confirmation before moving selected messages to another folder?
@ -409,9 +410,9 @@ test connection and display basic information about the selected profile mail en
text mail en Text text mail en Text
text mode mail en Text mode text mode mail en Text mode
text/plain mail en text/plain text/plain mail en text/plain
the %1 's acl removed from the %2! mail en The %1 's acl removed from the %2! the %1 's acl removed from the %2 mail en The %1 's acl removed from the %2
the %1 's acl, including its subfolders, removed from the %2! mail en The %1 's acl, including its subfolders, removed from the %2! the %1 's acl, including its subfolders, removed from the %2 mail en The %1 's acl, including its subfolders, removed from the %2
the folder %1 's acls saved! mail en The Folder %1 's ACLs saved! the folder %1 's acls saved mail en The Folder %1 's ACLs saved
the folder <b>%1</b> will be used, if there is nothing set here, and no valid predefine given. mail en The folder <b>%1</b> will be used, if there is nothing set here, and no valid predefine given. the folder <b>%1</b> will be used, if there is nothing set here, and no valid predefine given. mail en The folder <b>%1</b> will be used, if there is nothing set here, and no valid predefine given.
the message sender has requested a response to indicate that you have read this message. would you like to send a receipt? mail en The message sender has requested a response to indicate that you have read this message. Would you like to send a receipt? the message sender has requested a response to indicate that you have read this message. would you like to send a receipt? mail en The message sender has requested a response to indicate that you have read this message. Would you like to send a receipt?
the mimeparser can not parse this message. mail en The mimeparser can not parse this message. the mimeparser can not parse this message. mail en The mimeparser can not parse this message.