Specify a custom vacation subject
User can now specify a custom vacation subject. For Sieve servers implementing the variables extension, one can write ${subject} to insert the original subject in the auto reply. SOGoDefaultVacationSubject is a new domain defaults parameter used when the user doesn't specify a custom subject. Fixes #685, #1447pull/214/head
parent
49ed5bb462
commit
d8fc40217d
|
@ -1998,6 +1998,13 @@ message expiration.
|
||||||
See the _Cronjob — Vacation messages expiration_ section below for
|
See the _Cronjob — Vacation messages expiration_ section below for
|
||||||
details.
|
details.
|
||||||
|
|
||||||
|
|D |SOGoVacationDefaultSubject
|
||||||
|
|Parameter used to define a default vacation subject if user don't specify a
|
||||||
|
custom subject.
|
||||||
|
|
||||||
|
Defaults to the characters "Auto: " followed by the original subject when unset,
|
||||||
|
as stated by RFC 5230.
|
||||||
|
|
||||||
|D |SOGoVacationHeaderTemplateFile
|
|D |SOGoVacationHeaderTemplateFile
|
||||||
|Parameter used to specify the path of a text file whose content must be
|
|Parameter used to specify the path of a text file whose content must be
|
||||||
prepended to the user's vacation message. For example:
|
prepended to the user's vacation message. For example:
|
||||||
|
|
1
NEWS
1
NEWS
|
@ -5,6 +5,7 @@ New features
|
||||||
- [core] now possible to define default Sieve filters (#2949)
|
- [core] now possible to define default Sieve filters (#2949)
|
||||||
- [core] now possible to set vacation message start date (#3679)
|
- [core] now possible to set vacation message start date (#3679)
|
||||||
- [web] add a header and/or footer to the vacation message (#1961)
|
- [web] add a header and/or footer to the vacation message (#1961)
|
||||||
|
- [web] specify a custom subject for the vacation message (#685, #1447)
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
- [core] when restoring data using sogo-tool, regenerate Sieve script (#3029)
|
- [core] when restoring data using sogo-tool, regenerate Sieve script (#3029)
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
- (BOOL) forwardEnabled;
|
- (BOOL) forwardEnabled;
|
||||||
- (int) forwardConstraints;
|
- (int) forwardConstraints;
|
||||||
- (BOOL) vacationEnabled;
|
- (BOOL) vacationEnabled;
|
||||||
|
- (NSString *) vacationDefaultSubject;
|
||||||
- (NSString *) vacationHeaderTemplateFile;
|
- (NSString *) vacationHeaderTemplateFile;
|
||||||
- (NSString *) vacationFooterTemplateFile;
|
- (NSString *) vacationFooterTemplateFile;
|
||||||
- (NSString *) mailingMechanism;
|
- (NSString *) mailingMechanism;
|
||||||
|
|
|
@ -220,6 +220,11 @@
|
||||||
return [self boolForKey: @"SOGoVacationEnabled"];
|
return [self boolForKey: @"SOGoVacationEnabled"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString *) vacationDefaultSubject
|
||||||
|
{
|
||||||
|
return [self stringForKey: @"SOGoVacationDefaultSubject"];
|
||||||
|
}
|
||||||
|
|
||||||
- (NSString *) vacationHeaderTemplateFile
|
- (NSString *) vacationHeaderTemplateFile
|
||||||
{
|
{
|
||||||
return [self stringForKey: @"SOGoVacationHeaderTemplateFile"];
|
return [self stringForKey: @"SOGoVacationHeaderTemplateFile"];
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#import <SOGo/NSArray+Utilities.h>
|
#import <SOGo/NSArray+Utilities.h>
|
||||||
#import <SOGo/NSDictionary+Utilities.h>
|
#import <SOGo/NSDictionary+Utilities.h>
|
||||||
|
#import <SOGo/NSString+Utilities.h>
|
||||||
#import <SOGo/SOGoDomainDefaults.h>
|
#import <SOGo/SOGoDomainDefaults.h>
|
||||||
#import <SOGo/SOGoUser.h>
|
#import <SOGo/SOGoUser.h>
|
||||||
#import <SOGo/SOGoTextTemplateFile.h>
|
#import <SOGo/SOGoTextTemplateFile.h>
|
||||||
|
@ -834,19 +835,28 @@ static NSString *sieveScriptName = @"sogo";
|
||||||
{
|
{
|
||||||
NSMutableString *vacation_script;
|
NSMutableString *vacation_script;
|
||||||
NSArray *addresses;
|
NSArray *addresses;
|
||||||
NSString *text, *templateFilePath;
|
NSString *text, *templateFilePath, *customSubject;
|
||||||
SOGoTextTemplateFile *templateFile;
|
SOGoTextTemplateFile *templateFile;
|
||||||
|
|
||||||
BOOL ignore, alwaysSend;
|
BOOL ignore, alwaysSend, useCustomSubject;
|
||||||
int days, i;
|
int days, i;
|
||||||
|
|
||||||
days = [[values objectForKey: @"daysBetweenResponse"] intValue];
|
days = [[values objectForKey: @"daysBetweenResponse"] intValue];
|
||||||
addresses = [values objectForKey: @"autoReplyEmailAddresses"];
|
addresses = [values objectForKey: @"autoReplyEmailAddresses"];
|
||||||
alwaysSend = [[values objectForKey: @"alwaysSend"] boolValue];
|
alwaysSend = [[values objectForKey: @"alwaysSend"] boolValue];
|
||||||
ignore = [[values objectForKey: @"ignoreLists"] boolValue];
|
ignore = [[values objectForKey: @"ignoreLists"] boolValue];
|
||||||
|
useCustomSubject = [[values objectForKey: @"customSubjectEnabled"] boolValue];
|
||||||
|
customSubject = [values objectForKey: @"customSubject"];
|
||||||
text = [values objectForKey: @"autoReplyText"];
|
text = [values objectForKey: @"autoReplyText"];
|
||||||
b = YES;
|
b = YES;
|
||||||
|
|
||||||
|
if (!useCustomSubject)
|
||||||
|
{
|
||||||
|
// If user has not specified a custom subject, fallback to the domain's defaults
|
||||||
|
customSubject = [dd vacationDefaultSubject];
|
||||||
|
useCustomSubject = [customSubject length] > 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Add autoresponder header if configured */
|
/* Add autoresponder header if configured */
|
||||||
templateFilePath = [dd vacationHeaderTemplateFile];
|
templateFilePath = [dd vacationHeaderTemplateFile];
|
||||||
if (templateFilePath)
|
if (templateFilePath)
|
||||||
|
@ -874,10 +884,25 @@ static NSString *sieveScriptName = @"sogo";
|
||||||
|
|
||||||
// Skip mailing lists
|
// Skip mailing lists
|
||||||
if (ignore)
|
if (ignore)
|
||||||
[vacation_script appendString: @"if allof ( not exists [\"list-help\", \"list-unsubscribe\", \"list-subscribe\", \"list-owner\", \"list-post\", \"list-archive\", \"list-id\", \"Mailing-List\"], not header :comparator \"i;ascii-casemap\" :is \"Precedence\" [\"list\", \"bulk\", \"junk\"], not header :comparator \"i;ascii-casemap\" :matches \"To\" \"Multiple recipients of*\" ) {"];
|
[vacation_script appendString: @"if allof ( not exists [\"list-help\", \"list-unsubscribe\", \"list-subscribe\", \"list-owner\", \"list-post\", \"list-archive\", \"list-id\", \"Mailing-List\"], not header :comparator \"i;ascii-casemap\" :is \"Precedence\" [\"list\", \"bulk\", \"junk\"], not header :comparator \"i;ascii-casemap\" :matches \"To\" \"Multiple recipients of*\" ) { "];
|
||||||
|
|
||||||
[vacation_script appendFormat: @"vacation :days %d :addresses [", days];
|
|
||||||
|
|
||||||
|
// Custom subject
|
||||||
|
if (useCustomSubject)
|
||||||
|
{
|
||||||
|
if (([customSubject rangeOfString: @"${subject}"].location != NSNotFound) &&
|
||||||
|
[client hasCapability: @"variables"])
|
||||||
|
{
|
||||||
|
[req addObjectUniquely: @"variables"];
|
||||||
|
[vacation_script appendString: @"if header :matches \"Subject\" \"*\" { set \"subject\" \"${1}\"; } "];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[vacation_script appendFormat: @"vacation :days %d", days];
|
||||||
|
|
||||||
|
if (useCustomSubject)
|
||||||
|
[vacation_script appendFormat: @" :subject %@", [customSubject doubleQuotedString]];
|
||||||
|
|
||||||
|
[vacation_script appendString: @" :addresses ["];
|
||||||
for (i = 0; i < [addresses count]; i++)
|
for (i = 0; i < [addresses count]; i++)
|
||||||
{
|
{
|
||||||
[vacation_script appendFormat: @"\"%@\"", [addresses objectAtIndex: i]];
|
[vacation_script appendFormat: @"\"%@\"", [addresses objectAtIndex: i]];
|
||||||
|
|
|
@ -36,6 +36,9 @@
|
||||||
|
|
||||||
/* vacation (auto-reply) */
|
/* vacation (auto-reply) */
|
||||||
"Enable vacation auto reply" = "Enable vacation auto reply";
|
"Enable vacation auto reply" = "Enable vacation auto reply";
|
||||||
|
"Enable custom auto reply subject" = "Enable custom auto reply subject";
|
||||||
|
"Auto reply subject" = "Auto reply subject";
|
||||||
|
"You can write ${subject} to insert the original subject" = "You can write ${subject} to insert the original subject";
|
||||||
"Auto reply message" = "Auto reply message";
|
"Auto reply message" = "Auto reply message";
|
||||||
"Email addresses (separated by commas)" = "Email addresses (separated by commas)";
|
"Email addresses (separated by commas)" = "Email addresses (separated by commas)";
|
||||||
"Add default email addresses" = "Add default email addresses";
|
"Add default email addresses" = "Add default email addresses";
|
||||||
|
|
|
@ -72,11 +72,11 @@ static SoProduct *preferencesProduct = nil;
|
||||||
|
|
||||||
- (WOResponse *) jsonDefaultsAction
|
- (WOResponse *) jsonDefaultsAction
|
||||||
{
|
{
|
||||||
NSMutableDictionary *values, *account;
|
NSMutableDictionary *values, *account, *vacation;
|
||||||
SOGoUserDefaults *defaults;
|
SOGoUserDefaults *defaults;
|
||||||
SOGoDomainDefaults *domainDefaults;
|
SOGoDomainDefaults *domainDefaults;
|
||||||
NSMutableArray *accounts;
|
NSMutableArray *accounts;
|
||||||
NSDictionary *categoryLabels;
|
NSDictionary *categoryLabels, *vacationOptions;
|
||||||
NSDictionary *locale;
|
NSDictionary *locale;
|
||||||
|
|
||||||
if (!preferencesProduct)
|
if (!preferencesProduct)
|
||||||
|
@ -312,7 +312,7 @@ static SoProduct *preferencesProduct = nil;
|
||||||
values = [[[[defaults source] values] mutableCopy] autorelease];
|
values = [[[[defaults source] values] mutableCopy] autorelease];
|
||||||
|
|
||||||
//
|
//
|
||||||
// Expose additional information that must not be synchronized in the defaults
|
// Expose additional information that must *not* be synchronized in the defaults
|
||||||
//
|
//
|
||||||
|
|
||||||
// Expose the SOGoAppointmentSendEMailNotifications configuration parameter from the domain defaults
|
// Expose the SOGoAppointmentSendEMailNotifications configuration parameter from the domain defaults
|
||||||
|
@ -343,6 +343,18 @@ static SoProduct *preferencesProduct = nil;
|
||||||
[accounts insertObject: account atIndex: 0];
|
[accounts insertObject: account atIndex: 0];
|
||||||
[values setObject: accounts forKey: @"AuxiliaryMailAccounts"];
|
[values setObject: accounts forKey: @"AuxiliaryMailAccounts"];
|
||||||
|
|
||||||
|
// Add the domain's default vacation subject if user has not specified a custom subject
|
||||||
|
vacationOptions = [defaults vacationOptions];
|
||||||
|
if (![vacationOptions objectForKey: @"customSubject"] && [domainDefaults vacationDefaultSubject])
|
||||||
|
{
|
||||||
|
if (vacationOptions)
|
||||||
|
vacation = [NSMutableDictionary dictionaryWithDictionary: vacationOptions];
|
||||||
|
else
|
||||||
|
vacation = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
|
[vacation setObject: [domainDefaults vacationDefaultSubject] forKey: @"customSubject"];
|
||||||
|
[values setObject: vacation forKey: @"Vacation"];
|
||||||
|
}
|
||||||
|
|
||||||
return [self responseWithStatus: 200 andJSONRepresentation: values];
|
return [self responseWithStatus: 200 andJSONRepresentation: values];
|
||||||
}
|
}
|
||||||
|
|
|
@ -818,6 +818,24 @@
|
||||||
|
|
||||||
<div class="md-inline-form" layout="column" flex-offset="5" ng-show="app.preferences.defaults.Vacation.enabled == 1">
|
<div class="md-inline-form" layout="column" flex-offset="5" ng-show="app.preferences.defaults.Vacation.enabled == 1">
|
||||||
|
|
||||||
|
<div layout="row">
|
||||||
|
<md-checkbox
|
||||||
|
ng-model="app.preferences.defaults.Vacation.customSubjectEnabled"
|
||||||
|
ng-true-value="1"
|
||||||
|
ng-false-value="0"
|
||||||
|
label:aria-label="Enable custom auto reply subject">
|
||||||
|
<!-- enable auto reply subject --></md-checkbox>
|
||||||
|
<md-input-container class="md-block md-flex">
|
||||||
|
<label><var:string label:value="Auto reply subject"/></label>
|
||||||
|
<input type="text"
|
||||||
|
ng-disabled="!app.preferences.defaults.Vacation.customSubjectEnabled"
|
||||||
|
ng-model="app.preferences.defaults.Vacation.customSubject"/>
|
||||||
|
<div class="sg-hint" ng-show="app.sieveVariablesCapability">
|
||||||
|
<var:string label:value="You can write ${subject} to insert the original subject"/>
|
||||||
|
</div>
|
||||||
|
</md-input-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
<md-input-container class="md-block md-flex">
|
<md-input-container class="md-block md-flex">
|
||||||
<label><var:string label:value="Auto reply message"/></label>
|
<label><var:string label:value="Auto reply message"/></label>
|
||||||
<var:if condition="vacationHeader.length">
|
<var:if condition="vacationHeader.length">
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
vm.timeZonesList = window.timeZonesList;
|
vm.timeZonesList = window.timeZonesList;
|
||||||
vm.timeZonesListFilter = timeZonesListFilter;
|
vm.timeZonesListFilter = timeZonesListFilter;
|
||||||
vm.timeZonesSearchText = '';
|
vm.timeZonesSearchText = '';
|
||||||
|
vm.sieveVariablesCapability = ($window.sieveCapabilities.indexOf('variables') >= 0);
|
||||||
|
|
||||||
// Fetch a flatten version of the mailboxes list of the main account (0)
|
// Fetch a flatten version of the mailboxes list of the main account (0)
|
||||||
// This list will be forwarded to the Sieve filter controller
|
// This list will be forwarded to the Sieve filter controller
|
||||||
|
|
|
@ -16,14 +16,18 @@ md-input-container {
|
||||||
// Temporary fix for https://github.com/angular/material/issues/6214
|
// Temporary fix for https://github.com/angular/material/issues/6214
|
||||||
min-height: 0 !important;
|
min-height: 0 !important;
|
||||||
}
|
}
|
||||||
|
.sg-hint {
|
||||||
|
@extend .md-char-counter;
|
||||||
|
@include rtl(text-align, left, right);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
md-input-container .bgroup {
|
//md-input-container .bgroup {
|
||||||
display: block;
|
// display: block;
|
||||||
}
|
//}
|
||||||
.bgroup b {
|
//.bgroup b {
|
||||||
left-margin: -1.25em;
|
// left-margin: -1.25em;
|
||||||
}
|
//}
|
||||||
|
|
||||||
md-autocomplete .sg-input-no-message [md-floating-label] {
|
md-autocomplete .sg-input-no-message [md-floating-label] {
|
||||||
md-input-container {
|
md-input-container {
|
||||||
|
|
Loading…
Reference in New Issue