* Mail: new modus for vacation notice to not save message to INBOX and REST API to set vacation handling

This commit is contained in:
ralf 2023-07-31 16:24:58 +02:00
parent 7771710aee
commit 027ad398d6
9 changed files with 297 additions and 74 deletions

View File

@ -2312,8 +2312,8 @@ class CalDAV extends HTTP_WebDAV_Server
'%3F' => '?',
));
$ok = ($id || isset($_GET['add-member']) && $_SERVER['REQUEST_METHOD'] == 'POST') &&
($user || $user === 0) && in_array($app,array('addressbook','calendar','infolog','principals'));
$ok = ($id || isset($_GET['add-member']) && $_SERVER['REQUEST_METHOD'] === 'POST') &&
($user || $user === 0) && self::app_handler($app);
if ($this->debug)
{

View File

@ -1398,12 +1398,13 @@ class Imap extends Horde_Imap_Client_Socket implements Imap\PushIface
/**
* Set vacation message for given user
*
* @param int|string $_euser nummeric account_id or imap username
* @param int|string $_euser numeric account_id or imap username
* @param array $_vacation
* @param string $_scriptName =null
* @param string|null& $vacation_rule=null on return Sieve vacation rule
* @return boolean
*/
public function setVacationUser($_euser, array $_vacation, $_scriptName=null)
public function setVacationUser($_euser, array $_vacation, $_scriptName=null, string &$vacation_rule=null)
{
if ($this->debug) error_log(__METHOD__.' User:'.array2string($_euser).' Scriptname:'.array2string($_scriptName).' VacationMessage:'.array2string($_vacation));
@ -1418,7 +1419,7 @@ class Imap extends Horde_Imap_Client_Socket implements Imap\PushIface
$this->scriptName =& $this->sieve->scriptName;
$this->error =& $this->sieve->error;
}
$ret = $this->setVacation($_vacation, $_scriptName);
$ret = $this->setVacation($_vacation, $_scriptName, $vacation_rule);
return $ret;
}

View File

@ -184,15 +184,22 @@ class Script
}
break;
case "vacation" :
if (preg_match("/^ *#vacation&&(.*)&&(.*)&&(.*)&&(.*)&&(.*)/i",$line,$bits) ||
if (preg_match("/^ *#vacation&&(.*)&&(.*)&&(.*)&&(.*)&&(.*)&&(.*)/i",$line,$bits) ||
preg_match("/^ *#vacation&&(.*)&&(.*)&&(.*)&&(.*)&&(.*)/i",$line,$bits) ||
preg_match("/^ *#vacation&&(.*)&&(.*)&&(.*)&&(.*)/i",$line,$bits)) {
$vacation['days'] = $bits[1];
$vaddresslist = preg_replace("/\"|\s/","",$bits[2]);
$vaddresses = preg_split("/,/",$vaddresslist);
$vacation['text'] = $bits[3];
// <crnl>s will be encoded as \\n. undo this.
$vacation['text'] = preg_replace("/\\\\n/","\r\n",$vacation['text']);
if (($vacation['text'] = $bits[3]) === 'false')
{
$vacation['text'] = false;
}
else
{
// <crnl>s will be encoded as \\n. undo this.
$vacation['text'] = preg_replace("/\\\\n/","\r\n",$vacation['text']);
}
$vacation['modus'] = $bits[6] ?? null;
if (strpos($bits[4],'-')!== false)
{
@ -245,8 +252,11 @@ class Script
*
* @param Sieve $connection
* @param boolean $utf7imap_fileinto =false true: encode foldernames with utf7imap, default utf8
* @param string|null& $vac_rule on return vacation-rule in sieve syntax
* @param bool $throw_exceptions =false true: throw exception with error-message
* @return bool false on error with error-message in $this->error
*/
function updateScript (Sieve $connection, $utf7imap_fileinto=false)
function updateScript (Sieve $connection, $utf7imap_fileinto=false, &$vac_rule=null, bool $throw_exceptions=false)
{
#global $_SESSION,$default,$sieve;
global $default,$sieve;
@ -411,8 +421,8 @@ class Script
if ($this->vacation) {
$vacation = $this->vacation;
if (!$vacation['days']) $vacation['days'] = ($default->vacation_days ? $default->vacation_days:'');
if (!$vacation['text']) $vacation['text'] = ($default->vacation_text ? $default->vacation_text:'');
if (!$vacation['days']) $vacation['days'] = $default->vacation_days ?: '';
if (!$vacation['text']) $vacation['text'] = $default->vacation_text ?: '';
if (!$vacation['status']) $vacation['status'] = 'on';
// filter out invalid addresses.
@ -430,11 +440,12 @@ class Script
if (($vacation['status'] == 'on' && strlen(trim($vacation['text']))>0)|| $vacation['status'] == 'by_date') // +24*3600 to include the end_date day
{
$vacation_active = true;
$vac_rule = '';
if ($vacation['text'])
{
if ($this->extensions['regex'])
{
$newscriptbody .= "if header :regex ".'"X-Spam-Status" '.'"\\\\bYES\\\\b"'."{\n\tstop;\n}\n"; //stop vacation reply if it is spam
$vac_rule .= "if header :regex " . '"X-Spam-Status" ' . '"\\\\bYES\\\\b"' . "{\n\tstop;\n}\n"; //stop vacation reply if it is spam
$regexused = 1;
}
else
@ -442,56 +453,64 @@ class Script
// if there are no regex'es supported use a different Anti-Spam Rule: if X-Spam-Status holds
// additional spamscore information (e.g. BAYES) this rule may prevent Vacation notification
// TODO: refine rule without using regex
$newscriptbody .= "if header :contains ".'"X-Spam-Status" '.'"YES"'."{\n\tstop;\n}\n"; //stop vacation reply if it is spam
$vac_rule .= "if header :contains " . '"X-Spam-Status" ' . '"YES"' . "{\n\tstop;\n}\n"; //stop vacation reply if it is spam
}
}
if (trim($vacation['forwards'])) {
$if = array();
foreach($vacation['addresses'] as $addr) {
$if[] = 'address :contains ["To","TO","Cc","CC"] "'.trim($addr).'"';
}
$newscriptbody .= 'if anyof ('.implode(', ',$if).") {\n";
foreach(preg_split('/, ?/',$vacation['forwards']) as $addr) {
$newscriptbody .= "\tredirect \"".trim($addr)."\";\n";
}
$newscriptbody .= "\tkeep;\n}\n";
}
$vac_rule = "vacation :days " . $vacation['days'];
$first = 1;
if (!empty($vacation['addresses'][0]))
if (trim($vacation['forwards']))
{
$vac_rule .= " :addresses [";
foreach ($vacation['addresses'] as $vaddress) {
$if = array();
foreach ($vacation['addresses'] as $addr)
{
$if[] = 'address :contains ["To","TO","Cc","CC"] "' . trim($addr) . '"';
}
$vac_rule .= 'if anyof (' . implode(', ', $if) . ") {\n";
foreach (preg_split('/, ?/', $vacation['forwards']) as $addr)
{
$vac_rule .= "\tredirect \"" . trim($addr) . "\";\n";
}
$vac_rule .= "\tkeep;\n}\n";
}
if (!isset($vacation['modus']) || $vacation['modus'] !== 'store')
{
$vac_rule .= "vacation :days " . $vacation['days'];
$first = 1;
if (!empty($vacation['addresses'][0]))
{
$vac_rule .= " :addresses [";
foreach ($vacation['addresses'] as $vaddress)
{
if (!$first) $vac_rule .= ", ";
$vac_rule .= "\"" . trim($vaddress) . "\"";
$first = 0;
}
$vac_rule .= "] ";
}
$vac_rule .= "] ";
$message = $vacation['text'];
if ($vacation['status'] === 'by_date' && ($vacation['start_date'] || $vacation['end_date']))
{
$format_date = 'd M Y'; // see to it, that there is always a format, because if it is missing - no date will be output
if (!empty($GLOBALS['egw_info']['user']['preferences']['common']['dateformat'])) $format_date = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat'];
$message = str_replace(array('$$start$$', '$$end$$'), array(
date($format_date, $vacation['start_date']),
date($format_date, $vacation['end_date']),
), $message);
}
$vac_rule .= " text:\n" . $message . "\n.\n;\n\n";
}
$message = $vacation['text'];
if ($vacation['status'] === 'by_date' && ($vacation['start_date'] || $vacation['end_date']))
if (isset($vacation['modus']) && $vacation['modus'] === 'notice')
{
$format_date = 'd M Y'; // see to it, that there is always a format, because if it is missing - no date will be output
if (!empty($GLOBALS['egw_info']['user']['preferences']['common']['dateformat'])) $format_date = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat'];
$message = str_replace(array('$$start$$','$$end$$'),array(
date($format_date,$vacation['start_date']),
date($format_date,$vacation['end_date']),
),$message);
$vac_rule .= "discard;\n";
}
$vac_rule .= " text:\n" . $message . "\n.\n;\n\n";
if ($vacation['status'] === 'by_date' && $this->extensions['date'] && $vacation['start_date'] && $vacation['end_date'])
{
$newscriptbody .= "if allof (\n".
$vac_rule = "if allof (\n".
"currentdate :value \"ge\" \"date\" \"". date('Y-m-d', $vacation['start_date']) ."\",\n".
"currentdate :value \"le\" \"date\" \"". date('Y-m-d', $vacation['end_date']) ."\")\n".
"{\n".
$vac_rule."\n".
"}\n";
}
else
{
$newscriptbody .= $vac_rule;
}
$newscriptbody .= $vac_rule;
}
// update with any changes.
@ -624,10 +643,11 @@ class Script
$first = 0;
}
$vacation['text'] = preg_replace("/\r?\n/","\\n",$vacation['text']);
$vacation['text'] = $vacation['text'] === false ? 'false' : preg_replace("/\r?\n/","\\n", $vacation['text']);
$newscriptfoot .= "&&" . $vacation['text'] . "&&" .
($vacation['status']=='by_date' ? $vacation['start_date'].'-'.$vacation['end_date'] : $vacation['status']);
if ($vacation['forwards']) $newscriptfoot .= '&&' . $vacation['forwards'];
$newscriptfoot .= '&&' . ($vacation['forwards'] ?? '');
if (!empty($vacation['modus'])) $newscriptfoot .= '&&' . $vacation['modus'];
$newscriptfoot .= "\n";
}
if ($this->emailNotification) {
@ -651,6 +671,7 @@ class Script
$connection->installScript($this->name, $newscript, true);
}
catch (\Exception $e) {
if ($throw_exceptions) throw $e;
$this->errstr = 'updateScript: putscript failed: ' . $e->getMessage().($e->details?': '.$e->details:'');
if ($regexused && !$this->extensions['regex']) $this->errstr .= " REGEX is not an supported CAPABILITY";
error_log(__METHOD__.__LINE__.' # Error: ->'.$this->errstr);
@ -661,4 +682,4 @@ class Script
return true;
}
}
}

View File

@ -150,7 +150,7 @@ class Sieve extends Horde\ManageSieve
* @param array $_vacation
* @param string $_scriptName
*/
function setVacation(array $_vacation, $_scriptName=null)
function setVacation(array $_vacation, $_scriptName=null, &$vaction_rule=null, $throw_exception=false)
{
if ($this->debug)
{
@ -159,7 +159,7 @@ class Sieve extends Horde\ManageSieve
$script = $this->retrieveRules($_scriptName);
$script->debug = $this->debug;
$script->vacation = $_vacation;
$ret = $script->updateScript($this);
$ret = $script->updateScript($this, false, $vaction_rule, $throw_exception);
$this->error = $script->errstr;
return $ret;
}
@ -225,4 +225,4 @@ class Sieve extends Horde\ManageSieve
return $script;
}
}
}

View File

@ -133,4 +133,59 @@ Location: https://example.org/egroupware/groupdav.php/mail/attachment/<token>
"location": "/mail/attachments/<filename>--xM35lY"
}
```
</details>
- ```POST /mail[/<id>]/vacation``` enable or disable vacation message or forwarding
<details>
<summary>Example: Setting a vacation message with given start- and end-date</summary>
The content of the POST request is a JSON encoded object with following attributes
- ```status```: "on" (default, if not start/end), "off" or "by_date" (default, if start/end given)
- ```start```: start-date "YYYY-mm-dd", or e.g. "+2days" (optional)
- ```end```: end-date (last day of vacation) "YYYY-mm-dd" (optional)
- ```text```: vacation notice to the sender (can container $$start$$ and $$end$$ placeholders)
- ```modus```: "notice+store" (default) send vacation notice and store in INBOX, "notice": only send notice, "store": only store
- ```forwards```: array of strings with (RFC882) email addresses (optional, default no forwarding)
- ```addresses```: array of strings with (RFC882) email addresses (optional, default primary email address only)
- ```days```: integer, after how many days should a sender get the vacation message again (optional, otherwise default is used)
> The ```POST``` request is handled like a ```PATCH```, only the given attributes are replaced, use null to unset them.
```
curl -i https://example.org/egroupware/groupdav.php/mail/vacation --user <user> -X POST -H 'Content-Type: application/json' \
--data-binary '{"message":"I'm away from $$start$$ to $$end$$, will respond when I'm back.","start":"2023-01-01","end":"2023-01-10"}'
HTTP/1.1 200 Ok
{
"status": 200,
"message": "Vacation handling stored"
}
```
</details>
- ```GET /mail[/<id>]/vacation``` get current vacation message/handling
<details>
<summary>Example: Querying the current vacation handling</summary>
For an explanation of the returned attributes of the returned object, see the POST request.
```
curl -i https://example.org/egroupware/groupdav.php/mail/vacation --user <user> -H 'Accept: application/json'
HTTP/1.1 200 Ok
{
"start":"2023-01-01",
"end":"2023-01-10",
"status": "by_date",
"modus": "notice+store",
"text":"I'm away from $$start$$ to $$end$$, will respond when I'm back.",
"days": 5,
"addresses": ["me@example.org","webmaster@example.org"],
"forwards": ["hugo.meyer@example.org","sven@example.com"]
}
```
</details>

View File

@ -416,6 +416,7 @@ only name mail de Nur Name
only needed for some servers, that do not return all folders on root level queries to retrieve all folders for that level mail de wird nur für bestimmte Server gebraucht, die NICHT alle Ordner für Root-Knoten-Abfragen zurückgeben (damit wird in diesem Fall explizit auf Ordner in den Prefixes der Namespaces geprüft)
only one window mail de nur ein einziges Fenster
only send message, do not copy a version of the message to the configured sent folder mail de Versende Nachricht, kopiere sie nicht in den konfigurierten Gesendet-Ordner
only store message in inbox, do not send vacation notice mail de Nachricht nur im Posteingang speichern, keine Abwesenheitsnotiz versenden
open in html mode mail de In HTML-Modus öffnen
open in text mode mail de In Text-Modus öffnen
open with collabora office mail de Mit Collabora Online öffnen
@ -548,6 +549,8 @@ send a reject message: mail de Ablehnungs-E-Mail senden:
send files as mail de Dateien versenden als
send message and move to send folder (if configured) mail de Versende Nachricht und kopiere diese in den konfigurierten Gesendet-Ordner
send message pgp encrypted: requires keys from all recipients! mail de Nachricht PGP-verschlüsselt senden: benötigt Schlüssel von allen Empfängern!
send only a vacation notice, do not store message in inbox mail de Sende nur eine Abwesenheitsnotiz, die Nachricht selbst NICHT im Posteingang speichern
send vacation notice and store message in inbox mail de Sende eine Abwesenheitsnotiz und speichere die Mail im Posteingang
sender mail de Absender
sent mail de Gesendet
sent folder mail de Ordner für gesendete Nachrichten
@ -698,8 +701,8 @@ yes, offer copy option mail de Ja, mit Kopieroption
yes, only trigger connection reset mail de Ja, aber führe nur das zurücksetzen der Verbindung aus
yes, show all debug information available for the user mail de Ja, zeige alle dem Benutzer zugänglichen Informationen an.
yes, show basic info only mail de Ja, nur Basis Informationen anzeigen
you can select what info to be displayed on email tag mail de Sie können auswählen, welche Informationen auf dem E-Mail-Tag angezeigt werden sollen
you can either choose to save as infolog or tracker, not both. mail de Sie können eine E-Mail entweder als InfoLog-, ODER als Ticket-Eintrag speichern, nicht beides gleichzeitig.
you can select what info to be displayed on email tag mail de Sie können auswählen, welche Informationen auf dem E-Mail-Tag angezeigt werden sollen
you can use $$start$$ for the above start date and $$end$$ for the end date. mail de Sie können $$start$$ für das obige Start- und $$end$$ für das Enddatum verwenden.
you may add this certificate into your contact, if you trust this signature. mail de Sie können dieses Zertifikat in Ihren Kontakt speichern, wenn Sie dieser Signatur trauen.
you need to enter your s/mime passphrase to send this message. mail de Sie müssen Ihr S/MIME-Passwort eingeben um diese Nachricht zu senden.

View File

@ -416,6 +416,7 @@ only name mail en only name
only needed for some servers, that do not return all folders on root level queries to retrieve all folders for that level mail en only needed for some servers, that do not return all folders on root level queries to retrieve all folders for that level
only one window mail en only one window
only send message, do not copy a version of the message to the configured sent folder mail en only send message, do not copy a version of the message to the configured sent folder
only store message in inbox, do not send vacation notice mail en Only store message in INBOX, do NOT send vacation notice
open in html mode mail en Open in HTML mode
open in text mode mail en Open in Text mode
open with collabora office mail en Open with Collabora Office
@ -548,6 +549,8 @@ send a reject message: mail en Send a reject message:
send files as mail en Send files as
send message and move to send folder (if configured) mail en send message and move to send folder (if configured)
send message pgp encrypted: requires keys from all recipients! mail en Send message PGP encrypted: requires keys from all recipients!
send only a vacation notice, do not store message in inbox mail en Send only a vacation notice, do NOT store message in INBOX
send vacation notice and store message in inbox mail en Send vacation notice and store message in INBOX
sender mail en sender
sent mail en Sent
sent folder mail en sent folder
@ -698,8 +701,8 @@ yes, offer copy option mail en yes, offer copy option
yes, only trigger connection reset mail en yes, only trigger connection reset
yes, show all debug information available for the user mail en yes, show all debug information available for the user
yes, show basic info only mail en yes, show basic info only
you can select what info to be displayed on email tag mail en You can select what info to be displayed on email tag
you can either choose to save as infolog or tracker, not both. mail en You can either choose to save as infolog OR tracker, not both.
you can select what info to be displayed on email tag mail en You can select what info to be displayed on email tag
you can use $$start$$ for the above start date and $$end$$ for the end date. mail en You can use $$start$$ for the above start date and $$end$$ for the end date.
you may add this certificate into your contact, if you trust this signature. mail en You may add this certificate into your contact, if you trust this signature.
you need to enter your s/mime passphrase to send this message. mail en You need to enter your S/MIME passphrase to send this message.

View File

@ -31,7 +31,7 @@ class ApiHandler extends Api\CalDAV\Handler
/**
* Options for json_encode of responses
*/
const JSON_RESPONSE_OPTIONS = JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES;
const JSON_RESPONSE_OPTIONS = JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_THROW_ON_ERROR;
/**
* Handle post request for mail (send or compose mail and upload attachments)
@ -60,6 +60,10 @@ class ApiHandler extends Api\CalDAV\Handler
{
return self::storeAttachment($path, $options['stream'] ?? $options['content']);
}
elseif (preg_match('#^/mail(/(\d+))?/vacation/?$#', $path, $matches))
{
return self::updateVacation($user, $options['content'], $matches[2]);
}
elseif (preg_match('#^/mail(/(\d+))?(/compose)?#', $path, $matches))
{
$ident_id = $matches[2] ?? self::defaultIdentity($user);
@ -132,22 +136,70 @@ class ApiHandler extends Api\CalDAV\Handler
throw new \Exception('Not Found', 404);
}
catch (\Throwable $e) {
_egw_log_exception($e);
header('Content-Type: application/json');
echo json_encode([
'error' => $code = $e->getCode() ?: 500,
'message' => $e->getMessage(),
]+(empty($GLOBALS['egw_info']['server']['exception_show_trace']) ? [] : [
'trace' => array_map(static function($trace)
{
$trace['file'] = str_replace(EGW_SERVER_ROOT.'/', '', $trace['file']);
return $trace;
}, $e->getTrace())
]), self::JSON_RESPONSE_OPTIONS);
return (400 <= $code && $code < 600 ? $code : 500).' '.$e->getMessage();
return self::handleException($e);
}
}
/**
* Update vacation message/handling with JSON data given in $content
*
* @param int $user
* @param array $content
* @param int|null $identity
* @return bool
* @throws Api\Exception\AssertionFailed
* @throws Api\Exception\NotFound
*/
protected static function updateVacation(int $user, string $content, int $identity=null)
{
$account = self::getMailAccount($user, $identity);
$vacation = $account->imapServer()->getVacationUser($user);
if (!($update = json_decode($content, true, 3, JSON_THROW_ON_ERROR)))
{
return throw new \Exeception('Invalid request: no content', 400);
}
// Sieve class stores them as timestamps
foreach(['start', 'end'] as $name)
{
if (isset($update[$name]))
{
$vacation[$name.'_date'] = (new Api\DateTime($update[$name]))->format('ts');
if (empty($update['status'])) $update['status'] = 'by_date';
}
elseif (array_key_exists($name, $update))
{
$vacation[$name.'_date'] = null;
if (empty($update['status'])) $update['status'] = 'off';
}
unset($update[$name]);
}
// Sieve class stores them as comma-separated string
if (array_key_exists('forwards', $update))
{
$vacation['forwards'] = implode(',', $update['forwards'] ?? []);
unset($update['forwards']);
}
static $modi = ['notice+store', 'notice', 'store'];
if (isset($update['modus']) && !in_array($update['modus'], $modi))
{
throw new \Exception("Invalid value '$update[modus]' for attribute modus, allowed values are: '".implode("', '", $modi)."'", 400);
}
if (($invalid=array_diff(array_keys($update), ['start','end','status','modus','text','addresses','forwards','days'])))
{
throw new \Exception("Invalid attribute: ".implode(', ', $invalid), 400);
}
$vacation_rule = null;
$sieve = new Api\Mail\Sieve($account->imapServer());
$sieve->setVacation(array_merge($vacation, $update), null, $vacation_rule, true);
echo json_encode([
'status' => 200,
'message' => 'Vacation handling updated',
'vacation_rule' => $vacation_rule,
'vacation' => self::returnVacation($account->imapServer()->getVacationUser($user)),
], self::JSON_RESPONSE_OPTIONS);
return true;
}
/**
* Store uploaded attachment and return token
*
@ -324,13 +376,93 @@ class ApiHandler extends Api\CalDAV\Handler
*/
function get(&$options,$id,$user=null)
{
$path = rtrim($options['path'], '/');
if (empty($user))
{
$user = $GLOBALS['egw_info']['user']['account_id'];
}
else
{
$prefix = '/'.Api\Accounts::id2name($user);
if (str_starts_with($path, $prefix)) $path = substr($path, strlen($prefix));
}
header('Content-Type: application/json');
echo json_encode($all=iterator_to_array(Api\Mail\Account::identities([], true, 'name',
$user ?: $GLOBALS['egw_info']['user']['account_id'])), JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
return true;
try
{
switch ($path)
{
case '/mail':
echo json_encode(iterator_to_array(Api\Mail\Account::identities([], true, 'name', $user)),
self::JSON_RESPONSE_OPTIONS);
return true;
case preg_match('#^/mail(/(\d+))?/vacation$#', $path, $matches) === 1:
$account = self::getMailAccount($user, $matches[2] ?? null);
echo json_encode(self::returnVacation($account->imapServer()->getVacationUser($user)), self::JSON_RESPONSE_OPTIONS);
return true;
}
}
catch (\Throwable $e) {
return self::handleException($e);
}
return '501 Not Implemented';
}
protected static function returnVacation(array $vacation)
{
return array_filter([
'status' => $vacation['status'],
'start' => isset($vacation['start_date']) ? Api\DateTime::to($vacation['start_date'], 'Y-m-d') : null,
'end' => $vacation['end_date'] ? Api\DateTime::to($vacation['end_date'], 'Y-m-d') : null,
'text' => $vacation['text'],
'modus' => $vacation['modus'] ?? "notice+store",
'days' => (int)($vacation['days'] ?? 0),
'addresses' => $vacation['addresses'] ?? null,
'forwards' => empty($vacation['forwards']) ? [] : preg_split('/, ?/', $vacation['forwards']),
]);
}
/**
* Get mail account specified by identity or users default one
*
* @param int $user
* @param int|null $ident_id
* @return Api\Mail\Account
* @throws Api\Exception\NotFound
*/
protected static function getMailAccount(int $user, int $ident_id=null) : Api\Mail\Account
{
if (empty($ident_id))
{
return Api\Mail\Account::get_default();
}
$identity = Api\Mail\Account::read_identity($ident_id, false, $user);
return Api\Mail\Account::read($identity['acc_id']);
}
/**
* Handle exception by returning an appropriate HTTP status and JSON content with an error message
*
* @param \Throwable $e
* @return string
*/
protected function handleException(\Throwable $e) : string
{
_egw_log_exception($e);
header('Content-Type: application/json');
echo json_encode([
'error' => $code = $e->getCode() ?: 500,
'message' => $e->getMessage(),
]+(empty($GLOBALS['egw_info']['server']['exception_show_trace']) ? [] : [
'trace' => array_map(static function($trace)
{
$trace['file'] = str_replace(EGW_SERVER_ROOT.'/', '', $trace['file']);
return $trace;
}, $e->getTrace())
]), self::JSON_RESPONSE_OPTIONS);
return (400 <= $code && $code < 600 ? $code : 500).' '.$e->getMessage();
}
/**
* Handle get request for an applications entry
*

View File

@ -34,16 +34,24 @@
<et2-select-email id="forwards" allowFreeEntries="true" width="100%" multiple="true" searchUrl="EGroupware\Api\Etemplate\Widget\Taglist::ajax_email" placeholder="do not forward"></et2-select-email>
</row>
<row>
<et2-description value="Resend after how many days?"></et2-description>
<et2-number id="days" min="1" max="31" precision="0"></et2-number>
<et2-description value="Vacation notice"></et2-description>
<et2-select id="modus">
<option value="notice+store">Send vacation notice and store message in INBOX</option>
<option value="notice">Send only a vacation notice, do NOT store message in INBOX</option>
<option value="store">Only store message in INBOX, do NOT send vacation notice</option>
</et2-select>
</row>
<row>
<row valign="top">
<et2-description value="With message:"></et2-description>
<et2-vbox span="all">
<et2-textarea id="text" rows="10" class="et2_required" required="1"></et2-textarea>
<et2-description id="by_date_label" value="You can use $$start$$ for the above start date and $$end$$ for the end date."></et2-description>
</et2-vbox>
</row>
<row>
<et2-description value="Resend after how many days?"></et2-description>
<et2-number id="days" min="1" max="31" precision="0"></et2-number>
</row>
<row>
<et2-description value="Set it as default:" disabled="@is_not_admin_user"></et2-description>
<et2-checkbox id="set_as_default"></et2-checkbox>