Skip to content

Commit

Permalink
Optionally append original email subject to out-of-office auto-replies
Browse files Browse the repository at this point in the history
  • Loading branch information
marczi committed Jun 17, 2022
1 parent 7a93202 commit de15f5c
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 1 deletion.
10 changes: 10 additions & 0 deletions plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ function save()
$interval_types = rcube_utils::get_input_value('_action_interval_type', rcube_utils::INPUT_POST);
$from = rcube_utils::get_input_value('_action_from', rcube_utils::INPUT_POST, true);
$subject = rcube_utils::get_input_value('_action_subject', rcube_utils::INPUT_POST, true);
$subject_append_original = rcube_utils::get_input_value('_action_subject_append_original', rcube_utils::INPUT_POST);
$flags = rcube_utils::get_input_value('_action_flags', rcube_utils::INPUT_POST);
$varnames = rcube_utils::get_input_value('_action_varname', rcube_utils::INPUT_POST);
$varvalues = rcube_utils::get_input_value('_action_varvalue', rcube_utils::INPUT_POST);
Expand Down Expand Up @@ -1149,6 +1150,7 @@ function save()
$this->form['actions'][$i]['reason'] = str_replace("\r\n", "\n", $reason);
$this->form['actions'][$i]['from'] = $from[$idx];
$this->form['actions'][$i]['subject'] = $subject[$idx];
$this->form['actions'][$i]['subject_append_original'] = $subject_append_original[$idx];
$this->form['actions'][$i]['addresses'] = $addresses[$idx];
$this->form['actions'][$i][$interval_type] = $intervals[$idx];

Expand Down Expand Up @@ -2434,6 +2436,14 @@ function action_div($fid, $id, $div = true)
'size' => 35,
'class' => $this->error_class($id, 'action', 'subject', 'action_subject'),
]);
$out .= '<br>' . html::label('action_subject_append_original' . $id,
html::tag('input', array(
'type' => 'checkbox',
'name' => '_action_subject_append_original[' . $id . ']',
'id' => 'action_subject_append_original' . $id,
'value' => 1,
'checked' => isset($action['subject_append_original']) && $action['subject_append_original'] == 1,
)) . ' ' . rcube::Q($this->plugin->gettext('vacation.subjectappendoriginal')));
$out .= '<br><span class="label">' .rcube::Q($this->plugin->gettext('vacationfrom')) . '</span><br>';
$out .= html::tag('input', [
'type' => 'text',
Expand Down
86 changes: 85 additions & 1 deletion plugins/managesieve/lib/Roundcube/rcube_sieve_script.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ class rcube_sieve_script
// @TODO: virustest, mailbox
];

private $vacation_subject_var = 'subject';
private $vacation_subject_suffix = '${subject}';

/**
* Object constructor
*
Expand Down Expand Up @@ -209,6 +212,12 @@ public function as_text()
$exts = [];
$idx = 0;

$add_vacation_subject_rule = $this->should_generate_vacation_subject_rule();
if ($add_vacation_subject_rule) {
$this->set_var($this->vacation_subject_var, '');
}


if (!empty($this->vars)) {
if (in_array('variables', (array)$this->supported)) {
$has_vars = true;
Expand All @@ -232,6 +241,12 @@ public function as_text()
$imapflags = in_array('imap4flags', $this->supported) ? 'imap4flags' : 'imapflags';
$notify = in_array('enotify', $this->supported) ? 'enotify' : 'notify';

if ($add_vacation_subject_rule) {
$output .= 'if header :matches "subject" "*" {' . "\r\n" .
"\t" . 'set "' . $this->vacation_subject_var . '" "${1}";' . "\r\n" .
"}\r\n";
}

// rules
foreach ($this->content as $rule) {
$script = '';
Expand Down Expand Up @@ -567,7 +582,11 @@ public function as_text()
$action_script .= " :addresses " . self::escape_string($action['addresses']);
}
if (!empty($action['subject'])) {
$action_script .= " :subject " . self::escape_string($action['subject']);
$subject = trim($action['subject']);
if ($action['subject_append_original'] == 1) {
$subject .= $this->ends_with($subject, ':') ? ' ' : ': ' . $this->vacation_subject_suffix;
}
$action_script .= " :subject " . self::escape_string($subject);
}
if (!empty($action['handle'])) {
$action_script .= " :handle " . self::escape_string($action['handle']);
Expand Down Expand Up @@ -708,6 +727,13 @@ private function _parse_text($script)
}
}

// Skip the rule if it is a internal vacation rule (see more at is_vacation_subject_rule function)
if (!empty($rule)) {
if ($this->is_vacation_subject_rule($rule)) {
unset($rule);
}
}

if (!empty($rule)) {
$this->content[] = $rule;
}
Expand Down Expand Up @@ -974,6 +1000,12 @@ private function _parse_actions($content, &$position, $end = '}')
$vargs = ['seconds', 'days', 'addresses', 'subject', 'handle', 'from'];
$action += $this->action_arguments($tokens, $args, $vargs);

$subject = isset($action['subject']) ? $action['subject'] : '';
if (!empty($subject) && $this->ends_with($subject, ': ' . $this->vacation_subject_suffix)) {
$action['subject_append_original'] = 1;
$action['subject'] = trim(substr($subject, 0, -(strlen($this->vacation_subject_suffix) + 2)));
}

$result[] = $action;
break;

Expand Down Expand Up @@ -1478,4 +1510,56 @@ static function ltrim_position($content, $position, $br = true)

return $position;
}

/**
* Checks if the following unnamed rule is found:
*
* if header :matches "subject" "*" {
* set "subject" "${1}";
* }
*
* This is used by Sieverules and a commonly recommended solution to append the original subject to the vacation's subject
* This rule is usually preceeded by setting a global variable to empty, like this:
*
* set "subject" "";
* if header :matches "subject" "*" { ...
*/
private function is_vacation_subject_rule($rule) {
$name = isset($rule['name']) ? $rule['name'] : '';
$type = isset($rule['type']) ? $rule['type'] : '';
if (!empty($name) || $type != "if" || count($rule['tests']) != 1 || count($rule['actions']) != 1) {
return false;
}

$test = $rule['tests'][0];
$action = $rule['actions'][0];

return $test['test'] == 'header' && $test['type'] == 'matches' && $test['arg1'] == 'subject' && $test['arg2'] == '*'
&& $action['type'] == 'set' && $action['name'] == 'subject' && $action['value'] == '${1}';
}

/**
* Checks if there is any vacation rule that wants to append original subject to the vacation's subject
*/
private function should_generate_vacation_subject_rule() {
foreach ($this->content as $rule) {
foreach ($rule['actions'] as $action) {
if ($action['type'] == 'vacation' && isset($action['subject_append_original']) && $action['subject_append_original'] == 1) {
return true;
}
}
}
return false;
}

function ends_with($haystack, $needle) {
if ('' === $needle) {
return true;
}
if ('' === $haystack && '' !== $needle) {
return false;
}
$length = strlen($needle);
return strlen($haystack) >= $length && 0 === substr_compare($haystack, $needle, -$length, $length);
}
}
6 changes: 6 additions & 0 deletions plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ protected function vacation_post()
$status = rcube_utils::get_input_string('vacation_status', rcube_utils::INPUT_POST);
$from = rcube_utils::get_input_string('vacation_from', rcube_utils::INPUT_POST, true);
$subject = rcube_utils::get_input_string('vacation_subject', rcube_utils::INPUT_POST, true);
$subject_append_original = rcube_utils::get_input_value('vacation_subject_append_original', rcube_utils::INPUT_POST);
$reason = rcube_utils::get_input_string('vacation_reason', rcube_utils::INPUT_POST, true);
$addresses = rcube_utils::get_input_value('vacation_addresses', rcube_utils::INPUT_POST, true);
$interval = rcube_utils::get_input_string('vacation_interval', rcube_utils::INPUT_POST);
Expand All @@ -204,6 +205,7 @@ protected function vacation_post()
$vacation_action['type'] = 'vacation';
$vacation_action['reason'] = $this->strip_value(str_replace("\r\n", "\n", $reason), true);
$vacation_action['subject'] = trim($subject);
$vacation_action['subject_append_original'] = $subject_append_original == 1 ? 1 : NULL;
$vacation_action['from'] = trim($from);
$vacation_action['addresses'] = $addresses;
$vacation_action[$interval_type] = $interval;
Expand Down Expand Up @@ -393,6 +395,7 @@ public function vacation_form($attrib)
// form elements
$from = new html_inputfield(['name' => 'vacation_from', 'id' => 'vacation_from', 'size' => 50, 'class' => 'form-control']);
$subject = new html_inputfield(['name' => 'vacation_subject', 'id' => 'vacation_subject', 'size' => 50, 'class' => 'form-control']);
$subject_append_original = new html_inputfield(['name' => 'vacation_subject_append_original', 'id' => 'vacation_subject_append_original', 'type' => 'checkbox', 'class' => 'form-control']);
$reason = new html_textarea(['name' => 'vacation_reason', 'id' => 'vacation_reason', 'cols' => 60, 'rows' => 8]);
$interval = new html_inputfield(['name' => 'vacation_interval', 'id' => 'vacation_interval', 'size' => 5, 'class' => 'form-control']);
$addresses = '<textarea name="vacation_addresses" id="vacation_addresses" data-type="list" data-size="30" style="display: none">'
Expand Down Expand Up @@ -524,6 +527,9 @@ public function vacation_form($attrib)

$table->add('title', html::label('vacation_subject', $this->plugin->gettext('vacation.subject')));
$table->add(null, $subject->show(!empty($this->vacation['subject']) ? $this->vacation['subject'] : null));
$table->add('title', '');
$table->add(null, $subject_append_original->show('1', ['checked' => isset($this->vacation['subject_append_original']) && $this->vacation['subject_append_original'] == 1 ? 'checked' : ''])
. html::label('vacation_subject_append_original', $this->plugin->gettext('vacation.subjectappendoriginal')));
$table->add('title', html::label('vacation_reason', $this->plugin->gettext('vacation.body')));
$table->add(null, $reason->show(!empty($this->vacation['reason']) ? $this->vacation['reason'] : null));

Expand Down
1 change: 1 addition & 0 deletions plugins/managesieve/localization/de_DE.inc
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ $labels['vacation.reply'] = 'Antwort';
$labels['vacation.advanced'] = 'Erweiterte Einstellungen';
$labels['vacation.from'] = 'Antwort E-Mail-Adresse:';
$labels['vacation.subject'] = 'Betreff';
$labels['vacation.subjectappendoriginal'] = 'Anhängen des ursprünglichen Betreffs an die Antwort';
$labels['vacation.body'] = 'Nachricht';
$labels['vacation.start'] = 'Beginn der Abwesenheit';
$labels['vacation.end'] = 'Ende der Abwesenheit';
Expand Down
1 change: 1 addition & 0 deletions plugins/managesieve/localization/en_GB.inc
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ $labels['vacation.reply'] = 'Reply message';
$labels['vacation.advanced'] = 'Advanced settings';
$labels['vacation.from'] = 'Reply sender address';
$labels['vacation.subject'] = 'Subject';
$labels['vacation.subjectappendoriginal'] = 'Append original subject to the reply';
$labels['vacation.body'] = 'Body';
$labels['vacation.start'] = 'Start time';
$labels['vacation.end'] = 'End time';
Expand Down
1 change: 1 addition & 0 deletions plugins/managesieve/localization/en_US.inc
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ $labels['vacation.reply'] = 'Reply message';
$labels['vacation.advanced'] = 'Advanced settings';
$labels['vacation.from'] = 'Reply sender address';
$labels['vacation.subject'] = 'Subject';
$labels['vacation.subjectappendoriginal'] = 'Append original subject to the reply';
$labels['vacation.body'] = 'Body';
$labels['vacation.start'] = 'Start time';
$labels['vacation.end'] = 'End time';
Expand Down
1 change: 1 addition & 0 deletions plugins/managesieve/localization/hu_HU.inc
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ $labels['vacation.reply'] = 'Válasz az üzenetre';
$labels['vacation.advanced'] = 'Haladó beállítások';
$labels['vacation.from'] = 'Válasz küldőjének címe:';
$labels['vacation.subject'] = 'Tárgy';
$labels['vacation.subjectappendoriginal'] = 'Eredeti tárgy hozzáfűzése a válaszhoz';
$labels['vacation.body'] = 'Törzs';
$labels['vacation.start'] = 'Kezdő időpontja';
$labels['vacation.end'] = 'Befejezés időpontja';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require ["vacation","variables"];
set "subject" "";
if header :matches "subject" "*" {
set "subject" "${1}";
}
# rule:[Out of Office]
if true
{
vacation :days 3 :subject "Out of office: ${subject}" "Went for holiday";
}

0 comments on commit de15f5c

Please sign in to comment.