feat(notification): add enotify sieve script

This commit is contained in:
Hivert Quentin
2023-08-29 10:07:53 +02:00
parent 4f7c73143f
commit 82b69d1414
16 changed files with 223 additions and 21 deletions

View File

@@ -2285,6 +2285,13 @@ SOGoForwardConstraintsDomains = ("gmail.com", "googlemail.com");
will allow users to forward emails to only `gmail.com` and `googlemail.com` domains.
When empty or undefined, no constraints are imposed.
|D |SOGoNotificationEnabled
|Parameter used to activate the edition from the preferences window of
notifications for emails. Requires Sieve script support on the IMAP
host.
Defaults to `NO` when unset.
|D |SOGoSieveScriptsEnabled
|Parameter used to activate the edition from the preferences windows of
server-side mail filters. Requires Sieve script support on the IMAP

View File

@@ -53,6 +53,7 @@
- (BOOL) forwardEnabled;
- (int) forwardConstraints;
- (NSArray *) forwardConstraintsDomains;
- (BOOL) notificationEnabled;
- (BOOL) vacationEnabled;
- (BOOL) vacationPeriodEnabled;
- (NSString *) vacationDefaultSubject;

View File

@@ -225,6 +225,11 @@
return [self stringArrayForKey: @"SOGoForwardConstraintsDomains"];
}
- (BOOL) notificationEnabled
{
return [self boolForKey: @"SOGoNotificationEnabled"];
}
- (BOOL) vacationEnabled
{
return [self boolForKey: @"SOGoVacationEnabled"];

View File

@@ -206,7 +206,7 @@ static NSString *sieveScriptName = @"sogo";
@"imapflags", @"removeflag",
@"imapflags", @"flag",
@"vacation", @"vacation",
@"notify", @"notify",
@"notify", @"enotify",
@"fileinto", @"fileinto",
@"reject", @"reject",
@"regex", @"regex",
@@ -543,6 +543,12 @@ static NSString *sieveScriptName = @"sogo";
else if ([method isEqualToString: @"redirect"])
sieveAction = [NSString stringWithFormat: @"%@ %@",
method, [argument asSieveQuotedString]];
else if ([method isEqualToString: @"notify"])
{
argument = [NSString stringWithFormat: @"mailto:%@", argument];
sieveAction = [NSString stringWithFormat: @"%@ %@",
method, [argument asSieveQuotedString]];
}
else if ([method isEqualToString: @"reject"])
sieveAction = [NSString stringWithFormat: @"%@ %@",
method, [argument asSieveQuotedString]];
@@ -863,7 +869,7 @@ static NSString *sieveScriptName = @"sogo";
error = nil;
dd = [user domainDefaults];
if (!([dd sieveScriptsEnabled] || [dd vacationEnabled] || [dd forwardEnabled]))
if (!([dd sieveScriptsEnabled] || [dd vacationEnabled] || [dd forwardEnabled] || [dd notificationEnabled]))
return error;
req = [NSMutableArray arrayWithCapacity: 15];
@@ -1207,6 +1213,40 @@ static NSString *sieveScriptName = @"sogo";
}
}
// We handle mail notification
values = [ud notificationOptions];
if (values && [[values objectForKey: @"enabled"] boolValue])
{
// BOOL alwaysSend;
NSString *notify;
id addresses;
int i;
// alwaysSend = [[values objectForKey: @"alwaysSend"] boolValue];
b = YES;
[req addObjectUniquely: @"enotify"];
addresses = [values objectForKey: @"notificationAddress"];
if ([addresses isKindOfClass: [NSString class]])
addresses = [addresses componentsSeparatedByString: @","];
for (i = 0; i < [addresses count]; i++)
{
v = [addresses objectAtIndex: i];
if (v && [v length] > 0)
{
notify = [NSString stringWithFormat: @"notify \"mailto:%@\";\r\n", v];
// if (alwaysSend)
// [script insertString: notify atIndex: 0];
// else
[script appendString: notify];
}
}
}
// We handle header/footer Sieve scripts
if ((v = [dd sieveScriptHeaderTemplateFile]))
{

View File

@@ -222,6 +222,9 @@ extern NSString *SOGoPasswordRecoverySecondaryEmail;
- (void) setForwardOptions: (NSDictionary *) newValue;
- (NSDictionary *) forwardOptions;
- (void) setNotificationOptions: (NSDictionary *) newValue;
- (NSDictionary *) notificationOptions;
- (void) setMailLabelsColors: (NSDictionary *) newValues;
- (NSDictionary *) mailLabelsColors;

View File

@@ -989,6 +989,16 @@ NSString *SOGoPasswordRecoverySecondaryEmail = @"SecondaryEmail";
return [self dictionaryForKey: @"Forward"];
}
- (void) setNotificationOptions: (NSDictionary *) newValue
{
[self setObject: newValue forKey: @"Notification"];
}
- (NSDictionary *) notificationOptions
{
return [self dictionaryForKey: @"Notification"];
}
- (void) setContactsCategories: (NSArray *) newValues
{
[self setObject: newValues forKey: @"SOGoContactsCategories"];

View File

@@ -80,6 +80,9 @@
"You are not allowed to forward your messages to an internal email address." = "You are not allowed to forward your messages to an internal email address.";
"You are not allowed to forward your messages to this domain:" = "You are not allowed to forward your messages to this domain:";
/* notification */
"Notify incoming messsages" = "Notify incoming messsages";
/* d & t */
"Time Zone" = "Time Zone";
"Short Date Format" = "Short Date Format";
@@ -411,6 +414,7 @@
"Keep the message" = "Keep the message";
"Forward the message to" = "Forward the message to";
"Notify the message to" = "Notify the message to";
/* Input field label of "forward" mail filter action */
"Email" = "Email";

View File

@@ -68,7 +68,7 @@
= "La date de fin de la réponse automatique doit être dans le futur.";
/* forward messages */
"Forward incoming messages" = "Transférer les messages entrant";
"Forward incoming messages" = "Transférer les messages entrants";
"Always forward" = "Toujours transférer";
"Incoming messages are forwarded prior to apply your filters." = "Le courrier entrant est transféré d'appliquer vos filtres.";
"Keep a copy" = "Garder une copie";
@@ -80,6 +80,9 @@
"You are not allowed to forward your messages to an internal email address." = "Il est interdit de renvoyer vos messages vers une adresse interne.";
"You are not allowed to forward your messages to this domain:" = "Vous ne pouvez pas transférer vos messages à ce domaine :";
/* notify messages */
"Notify incoming messsages" = "Notifier les messages entrants";
/* d & t */
"Time Zone" = "Fuseau horaire";
"Short Date Format" = "Style de date courte";
@@ -411,6 +414,7 @@
"Keep the message" = "Conserver le message";
"Forward the message to" = "Faire suivre le message à";
"Notify the message to" = "Notifier le message à";
/* Input field label of "forward" mail filter action */
"Email" = "Email";

View File

@@ -395,9 +395,14 @@ static SoProduct *preferencesProduct = nil;
}
if ([domainDefaults forwardEnabled] && ![defaults forwardOptions])
{
[defaults setForwardOptions: [NSDictionary new]];
}
{
[defaults setForwardOptions: [NSDictionary new]];
}
if ([domainDefaults notificationEnabled] && ![defaults notificationOptions])
{
[defaults setNotificationOptions: [NSDictionary new]];
}
if ([[defaults source] dirty])
[defaults synchronize];

View File

@@ -42,7 +42,7 @@
// Sieve filtering
NSArray *daysOfWeek, *daysBetweenResponsesList;
NSArray *sieveFilters;
NSMutableDictionary *vacationOptions, *forwardOptions;
NSMutableDictionary *vacationOptions, *forwardOptions, *notificationOptions;
BOOL mailCustomFromEnabled;
BOOL forwardEnabled;

View File

@@ -136,13 +136,20 @@ static NSArray *reminderValues = nil;
vacationOptions = [NSMutableDictionary new];
}
if ([dd forwardEnabled])
if ([dd forwardEnabled])
{
forwardOptions = [[userDefaults forwardOptions] mutableCopy];
if (!forwardOptions)
forwardOptions = [NSMutableDictionary new];
}
if ([dd notificationEnabled])
{
notificationOptions = [[userDefaults notificationOptions] mutableCopy];
if (!notificationOptions)
notificationOptions = [NSMutableDictionary new];
}
mailCustomFromEnabled = [dd mailCustomFromEnabled];
forwardEnabled = [dd forwardEnabled];
@@ -160,6 +167,7 @@ static NSArray *reminderValues = nil;
[sieveFilters release];
[vacationOptions release];
[forwardOptions release];
[notificationOptions release];
[daysOfWeek release];
[addressBooksIDWithDisplayName release];
[client release];
@@ -989,6 +997,15 @@ static NSArray *reminderValues = nil;
return [domains jsonRepresentation];
}
/* mail notifications */
//
// Used by templates
//
- (BOOL) isNotificationEnabled
{
return [[user domainDefaults] notificationEnabled];
}
//
// Used by templates
//
@@ -1727,7 +1744,8 @@ static NSArray *reminderValues = nil;
dd = [[context activeUser] domainDefaults];
// We check if the Sieve server is available *ONLY* if at least one of the option is enabled
if (!([dd sieveScriptsEnabled] || [dd vacationEnabled] || [dd forwardEnabled]) || [self _isSieveServerAvailable])
if (([dd sieveScriptsEnabled] || [dd vacationEnabled] || [dd forwardEnabled] || [dd notificationEnabled])
|| [self _isSieveServerAvailable])
{
BOOL forceActivation = ![[v objectForKey: @"hasActiveExternalSieveScripts"] boolValue];

View File

@@ -130,6 +130,12 @@
<input type="text" ng-model="action.argument" required="required"/>
</md-input-container>
<!-- NOTIFY MESSAGE TO -->
<md-input-container class="md-block" flex="50" ng-if="action.method == 'notify'" >
<label><var:string label:value="Email"/></label>
<input type="text" ng-model="action.argument" required="required"/>
</md-input-container>
<!-- DISCARD -->
<!-- nada -->

View File

@@ -16,6 +16,7 @@
<script type="text/javascript">
var mailCustomFromEnabled = <var:string value="mailCustomFromEnabled" const:escapeHTML="NO"/>;
var forwardEnabled = <var:string value="forwardEnabled" const:escapeHTML="NO"/>;
var notificationEnabled = <var:string value="notificationEnabled" const:escapeHTML="NO"/>;
var vacationEnabled = <var:string value="isVacationEnabled" const:escapeHTML="NO"/>;
var timeZonesList = <var:string value="timeZonesList" const:escapeHTML="NO"/>;
var defaultEmailAddresses = <var:string value="defaultEmailAddresses" const:escapeHTML="NO"/>;
@@ -1325,7 +1326,7 @@
id="forwardAddress"
name="forwardAddress"
ng-model="app.preferences.defaults.Forward.forwardAddress"
md-transform-chip="app.addRecipient($chip)"
md-transform-chip="app.addRecipient($chip, 'forward')"
ng-required="app.preferences.defaults.Forward.enabled == 1"
md-separator-keys="app.emailSeparatorKeys"
md-add-on-blur="true"
@@ -1334,10 +1335,9 @@
<md-autocomplete
ng-keydown="app.ignoreReturn($event)"
md-menu-class="md-2-line"
md-search-text="app.autocompleteForward.searchText"
md-selected-item="app.autocompleteForward.selected"
md-items="user in app.contactFilter(app.autocompleteForward.searchText)"
md-item-text="app.addRecipient(user)"
md-search-text="app.autocomplete.forward.searchText"
md-selected-item="app.autocomplete.forward.selected"
md-items="user in app.contactFilter(app.autocomplete.forward.searchText)"
var:md-min-length="minimumSearchLength"
md-delay="150"
md-no-cache="true"
@@ -1346,8 +1346,8 @@
<md-item-template>
<div class="sg-tile-content">
<div class="sg-md-subhead">
<div md-highlight-text="app.autocompleteForward.searchText"
md-highlight-flags="gi">{{ user.$shortFormat(app.autocompleteForward.searchText) }}</div>
<div md-highlight-text="app.autocomplete.forward.searchText"
md-highlight-flags="gi">{{ user.$shortFormat(app.autocomplete.forward.searchText) }}</div>
</div>
<div class="sg-md-body"
md-colors="::{color: 'default-background-500'}">{{ user.containername }}</div>
@@ -1382,6 +1382,67 @@
</var:if>
<!-- END OF MAIL > FORWARD -->
<!-- MAIL > NOTIFICATIONS -->
<var:if condition="isNotificationEnabled">
<md-tab id="mailNotificationTab" aria-controls="mailNotificationTab-content" label:label="Notification">
<md-content class="md-padding"
ng-include="'hasActiveExternalSieveScripts.html'"
ng-show="app.preferences.defaults.hasActiveExternalSieveScripts">
<!-- external Sieve script detected -->
</md-content>
<div role="tabpanel" aria-labelledby="mailNotificationView"
id="mailNotificationView-content" class="md-padding">
<md-checkbox
ng-model="app.preferences.defaults.Notification.enabled"
ng-true-value="1"
ng-false-value="0">
<var:string label:value="Notify incoming messsages"/>
</md-checkbox>
<div flex-offset="5" ng-show="app.preferences.defaults.Notification.enabled == 1">
<md-input-container class="md-block md-flex">
<label><var:string label:value="Email addresses"/></label>
<md-chips
id="notificationAddress"
name="notificationAddress"
ng-model="app.preferences.defaults.Notification.notificationAddress"
md-transform-chip="app.addRecipient($chip, 'notification')"
ng-required="app.preferences.defaults.Notification.enabled == 1"
md-separator-keys="app.emailSeparatorKeys"
md-add-on-blur="true"
md-autocomplete-snap="width">
<!-- notifications addresses -->
<md-autocomplete
ng-keydown="app.ignoreReturn($event)"
md-menu-class="md-2-line"
md-search-text="app.autocomplete.notification.searchText"
md-selected-item="app.autocomplete.notification.selected"
md-items="user in app.contactFilter(app.autocomplete.notification.searchText)"
var:md-min-length="minimumSearchLength"
md-delay="150"
md-no-cache="true"
label:placeholder="Enter an email"
label:secondary-placeholder="Add another email">
<md-item-template>
<div class="sg-tile-content">
<div class="sg-md-subhead">
<div md-highlight-text="app.autocomplete.notification.searchText"
md-highlight-flags="gi">{{ user.$shortFormat(app.autocomplete.notification.searchText) }}</div>
</div>
<div class="sg-md-body"
md-colors="::{color: 'default-background-500'}">{{ user.containername }}</div>
</div>
</md-item-template>
</md-autocomplete>
</md-chips>
</md-input-container>
</div>
</div>
</md-tab>
</var:if>
<!-- END OF MAIL > FORWARD -->
</md-tabs>
</var:if>
</script>

View File

@@ -12,6 +12,7 @@
var vm = this,
sieveCapabilities = $window.sieveCapabilities,
forwardEnabled = $window.forwardEnabled,
notificationEnabled = $window.notificationEnabled,
vacationEnabled = $window.vacationEnabled;
this.filter = filter;
@@ -40,6 +41,9 @@
if (forwardEnabled)
this.methodLabels.redirect = l("Forward the message to");
if (notificationEnabled)
this.methodLabels.notify = l("Notify the message to");
//if (vacationEnabled)
// this.methodLabels.vacation = l("Send a vacation message");
@@ -59,6 +63,7 @@
"keep",
"discard",
"redirect",
"notify",
"reject"
];
this.methods = _.intersection(this.methods, _.keys(this.methodLabels));
@@ -112,6 +117,15 @@
this.invalid = err.message;
return false;
}
try {
_.forEach(_.filter(this.filter.actions, { 'method': 'notify' }), function (action) {
validateForwardAddress(action.argument);
});
} catch (err) {
//Dialog.alert(l('Error'), err);
this.invalid = err.message;
return false;
}
}
$mdDialog.hide();
};

View File

@@ -150,6 +150,13 @@
data.Forward.forwardAddress = [];
}
if (data.Notification) {
if (angular.isString(data.Notification.notificationAddress))
data.Notification.notificationAddress = data.Notification.notificationAddress.split(/, */);
else if (!angular.isArray(data.Notification.notificationAddress))
data.Notification.notificationAddress = [];
}
// Split calendar categories colors keys and values
if (angular.isUndefined(data.SOGoCalendarCategories))
data.SOGoCalendarCategories = [];
@@ -865,6 +872,9 @@
if (preferences.defaults.Forward && preferences.defaults.Forward.forwardAddress)
preferences.defaults.Forward.forwardAddress = _.compact(preferences.defaults.Forward.forwardAddress);
if (preferences.defaults.Notification && preferences.defaults.Notification.notificationAddress)
preferences.defaults.Notification.notificationAddress = _.compact(preferences.defaults.Notification.notificationAddress);
// Merge back calendar categories colors keys and values
preferences.defaults.SOGoCalendarCategoriesColors = {};
_.forEach(preferences.defaults.SOGoCalendarCategories, function(key, i) {

View File

@@ -17,7 +17,7 @@
this.timeZonesList = $window.timeZonesList;
this.timeZonesSearchText = '';
this.addressesSearchText = '';
this.autocompleteForward = {};
this.autocomplete = {forward: [], notification: []};
this.mailLabelKeyRE = new RegExp(/^(?!^_\$)[^(){} %*\"\\\\]*?$/);
this.emailSeparatorKeys = Preferences.defaults.emailSeparatorKeys;
if (Preferences.defaults.SOGoMailAutoMarkAsReadMode == 'delay')
@@ -415,6 +415,20 @@
}
}
// We check if we're allowed or not to notify based on the domain defaults
if (this.preferences.defaults.Notification && this.preferences.defaults.Notification.enabled &&
this.preferences.defaults.Notification.notificationAddress) {
addresses = this.preferences.defaults.Notification.notificationAddress;
try {
for (i = 0; i < addresses.length; i++) {
validateForwardAddress(addresses[i]);
}
} catch (err) {
Dialog.alert(l('Error'), err);
sendForm = false;
}
}
// IMAP labels must be unique
if (this.preferences.defaults.SOGoMailLabelsColorsKeys.length !=
this.preferences.defaults.SOGoMailLabelsColorsValues.length ||
@@ -650,10 +664,10 @@
}
};
this.addRecipient = function (contact) {
this.addRecipient = function (contact, element) {
var recipients, recipient, list, i, address;
recipients = this.preferences.defaults.Forward.forwardAddress;
recipients = this.autocomplete[element];
if (angular.isString(contact)) {
// Examples that are handled:
@@ -678,7 +692,7 @@
if (address && recipients.indexOf(address) < 0)
recipients.push(address);
return null;
return address;
}
if (contact.$isList({expandable: true})) {
@@ -712,7 +726,7 @@
else {
recipient = contact.$shortFormat();
}
if (recipient)
return recipient;
else