mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-04-04 12:58:50 +00:00
feat(notification): add enotify sieve script
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
- (BOOL) forwardEnabled;
|
||||
- (int) forwardConstraints;
|
||||
- (NSArray *) forwardConstraintsDomains;
|
||||
- (BOOL) notificationEnabled;
|
||||
- (BOOL) vacationEnabled;
|
||||
- (BOOL) vacationPeriodEnabled;
|
||||
- (NSString *) vacationDefaultSubject;
|
||||
|
||||
@@ -225,6 +225,11 @@
|
||||
return [self stringArrayForKey: @"SOGoForwardConstraintsDomains"];
|
||||
}
|
||||
|
||||
- (BOOL) notificationEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoNotificationEnabled"];
|
||||
}
|
||||
|
||||
- (BOOL) vacationEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoVacationEnabled"];
|
||||
|
||||
@@ -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]))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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"];
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
// Sieve filtering
|
||||
NSArray *daysOfWeek, *daysBetweenResponsesList;
|
||||
NSArray *sieveFilters;
|
||||
NSMutableDictionary *vacationOptions, *forwardOptions;
|
||||
NSMutableDictionary *vacationOptions, *forwardOptions, *notificationOptions;
|
||||
|
||||
BOOL mailCustomFromEnabled;
|
||||
BOOL forwardEnabled;
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user