diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index 0e373f4f3..6387cfb41 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -431,7 +431,7 @@ See `memcached_servers_parse(3)` for details on the syntax. |S |SOGoCacheCleanupInterval |Parameter used to set the expiration (in seconds) of each object in the -cache. +cache. Default value is `NO`. Defaults to `300`. @@ -1678,6 +1678,9 @@ URL could be set to something like: See the "EMail reminders" section in this document for more information. +|S |SOGoDisableMailCleaning +|Parameter used to disable the feature 'remove mails older than X days' introduced in 5.12 + |S |SOGoDisableOrganizerEventCheck |Parameter used to disable organizer's calendar event check diff --git a/SoObjects/Mailer/SOGoMailAccount.h b/SoObjects/Mailer/SOGoMailAccount.h index 4c8e86d6e..46c1bb0c6 100644 --- a/SoObjects/Mailer/SOGoMailAccount.h +++ b/SoObjects/Mailer/SOGoMailAccount.h @@ -104,6 +104,7 @@ typedef enum { - (NSArray *) toManyRelationshipKeysWithNamespaces: (BOOL) withNSs; - (NSArray *) allFolderPaths: (SOGoMailListingMode) theListingMode; +- (NSArray *) allFolderPaths: (SOGoMailListingMode) theListingMode onlyRoot: (BOOL) onlyRoot; - (NSArray *) allFoldersMetadata: (SOGoMailListingMode) theListingMode; - (NSDictionary *) imapFolderGUIDs; diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m index aa2a30b0c..2b4e2a34a 100644 --- a/SoObjects/Mailer/SOGoMailAccount.m +++ b/SoObjects/Mailer/SOGoMailAccount.m @@ -378,14 +378,21 @@ static NSString *inboxFolderName = @"INBOX"; return folders; } -// -// -// -- (NSArray *) allFolderPaths: (SOGoMailListingMode) theListingMode +- (NSArray *) allFolderPaths: (SOGoMailListingMode) theListingMode { - NSMutableArray *folderPaths, *namespaces; + return [self allFolderPaths: theListingMode onlyRoot: NO]; +} + +// +// +// +- (NSArray *) allFolderPaths: (SOGoMailListingMode) theListingMode onlyRoot: (BOOL) onlyRoot +{ + NSMutableArray *folderPaths, *namespaces, *filteredFolders; NSArray *folders, *mainFolders; NSString *namespace; + NSString *folder; + NSUInteger slashCount; BOOL subscribedOnly; int count, max; @@ -401,10 +408,10 @@ static NSString *inboxFolderName = @"INBOX"; onlySubscribedFolders: YES]; max = [folders count]; for (count = 0; count < max; count++) - { - [subscribedFolders setObject: [NSNull null] - forKey: [folders objectAtIndex: count]]; - } + { + [subscribedFolders setObject: [NSNull null] + forKey: [folders objectAtIndex: count]]; + } [[self imap4Connection] flushFolderHierarchyCache]; } @@ -418,6 +425,17 @@ static NSString *inboxFolderName = @"INBOX"; nil] stringsWithFormat: @"/%@"]; folders = [[self imap4Connection] allFoldersForURL: [self imap4URL] onlySubscribedFolders: subscribedOnly]; + if (onlyRoot) { + filteredFolders = [[NSMutableArray alloc] init]; + for (folder in folders) { + slashCount = [[folder componentsSeparatedByString:@"/"] count] - 1; + if (slashCount <= 1) { + [filteredFolders addObject: folder]; + } + } + folders = [NSArray arrayWithArray: filteredFolders]; + [filteredFolders release]; + } folderPaths = [folders mutableCopy]; [folderPaths autorelease]; diff --git a/SoObjects/SOGo/SOGoSystemDefaults.h b/SoObjects/SOGo/SOGoSystemDefaults.h index e3e2c8d2c..72e063a64 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.h +++ b/SoObjects/SOGo/SOGoSystemDefaults.h @@ -142,6 +142,8 @@ NSComparisonResult languageSort(id el1, id el2, void *context); - (NSString *)urlCreateAccount; +- (BOOL)disableMailCleaning; + @end #endif /* SOGOSYSTEMDEFAULTS_H */ diff --git a/SoObjects/SOGo/SOGoSystemDefaults.m b/SoObjects/SOGo/SOGoSystemDefaults.m index 61c299245..87d63f85e 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.m +++ b/SoObjects/SOGo/SOGoSystemDefaults.m @@ -933,4 +933,9 @@ NSComparisonResult languageSort(id el1, id el2, void *context) return [self stringForKey: @"SOGoURLCreateAccount"]; } +- (BOOL) disableMailCleaning +{ + return [self boolForKey: @"SOGoDisableMailCleaning"]; +} + @end diff --git a/UI/MailerUI/English.lproj/Localizable.strings b/UI/MailerUI/English.lproj/Localizable.strings index f71b108aa..bafd2d681 100644 --- a/UI/MailerUI/English.lproj/Localizable.strings +++ b/UI/MailerUI/English.lproj/Localizable.strings @@ -516,4 +516,16 @@ "hotkey_forward" = "f"; /* Email deletion */ -"Removal of old emails..." = "Removal of old emails..."; \ No newline at end of file +"Clean folder" = "Clean folder"; +"Clean mailbox" = "Clean mailbox"; +"Delete permanently (do not use trash)" = "Delete permanently (do not use trash)"; +"Apply to subfolders" = "Apply to subfolders"; +"Delete e-mails older than" = "Delete e-mails older than"; +"3 months" = "3 months"; +"6 months" = "6 months"; +"9 months" = "9 months"; +"1 year" = "1 year"; +"Custom" = "Custom"; +"You are about to permanently delete some messages. Are you sure you want to proceed with this action?" = "You are about to permanently delete some messages. Are you sure you want to proceed with this action?"; +"Apply" = "Apply"; +"%{0} message(s) deleted" = "%{0} message(s) deleted"; \ No newline at end of file diff --git a/UI/MailerUI/French.lproj/Localizable.strings b/UI/MailerUI/French.lproj/Localizable.strings index ac6755cf5..e70f62520 100644 --- a/UI/MailerUI/French.lproj/Localizable.strings +++ b/UI/MailerUI/French.lproj/Localizable.strings @@ -516,4 +516,16 @@ "hotkey_forward" = "f"; /* Email deletion */ -"Removal of old emails..." = "Suppression des anciens emails..."; \ No newline at end of file +"Clean folder" = "Nettoyer le dossier"; +"Clean mailbox" = "Nettoyer la boite mail"; +"Delete permanently (do not use trash)" = "Supprimer définitivement (ne pas utiliser la corbeille)"; +"Apply to subfolders" = "Appliquer aux sous-dossiers"; +"Delete e-mails older than" = "Supprimer les e-mails plus vieux de"; +"3 months" = "3 mois"; +"6 months" = "6 mois"; +"9 months" = "9 mois"; +"1 year" = "1 an"; +"Custom" = "Personnalisé"; +"You are about to permanently delete some messages. Are you sure you want to proceed with this action?" = "Vous êtes sur le point de supprimer définitivement certains messages. Êtes-vous certain de vouloir effectuer cette action ?"; +"Apply" = "Appliquer"; +"%{0} message(s) deleted" = "%{0} message(s) supprimés"; \ No newline at end of file diff --git a/UI/MailerUI/UIxMailFolderActions.m b/UI/MailerUI/UIxMailFolderActions.m index 7a6f3d997..60bb13e24 100644 --- a/UI/MailerUI/UIxMailFolderActions.m +++ b/UI/MailerUI/UIxMailFolderActions.m @@ -21,6 +21,7 @@ #import #import #import +#import #import #import @@ -1300,20 +1301,125 @@ return [self _markMessagesAsJunkOrNotJunk: NO]; } -- (WOResponse *) cleanMailboxAction -{ - NSString *searchString; - EOQualifier *searchQualifier; +- (BOOL)isDateStringValid: (NSString *)dateString { + NSString *dateRegex = @"^\\d{4}-\\d{2}-\\d{2}$"; + NSPredicate *dateTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", dateRegex]; + return [dateTest evaluateWithObject:dateString]; +} - searchString = [NSString stringWithFormat: @"(date >= (NSCalendarDate)\"%@\")", @"2020-01-12"]; - searchQualifier = [EOQualifier qualifierWithQualifierFormat: searchString]; +- (void) cleanFolderWithFolder: (SOGoMailFolder *)folder + withQualifier: (EOQualifier *)qualifier + recursive: (BOOL)isRecursive + withTrashFolder: (BOOL)useTrashFolder + exception: (NSException **)exception + counter: (NSUInteger *)counter { + NSArray *results, *subfolders; + NSUInteger i; + SOGoMailFolder *newFolder; + NSString *folderName; + NSException *e; - id foo; - foo = [[self clientObject] fetchUIDsMatchingQualifier: searchQualifier - sortOrdering: @"date" + results = [folder fetchUIDsMatchingQualifier: qualifier + sortOrdering: @"REVERSE DATE" threaded: NO]; - return [self responseWithStatus: 200]; + if (results && [results count] > 0) { + e = [folder deleteUIDs: results + useTrashFolder: &useTrashFolder + inContext: [self context]]; + [folder expunge]; + [self logWithFormat: @"Removed %d messages in %@ (%@)", [results count], [folder nameInContainer], results]; + *counter += [results count]; + if (e) { + [self errorWithFormat: @"Error while cleaning mailbox : %@", e]; + *exception = e; + } + } + + if (isRecursive) { + subfolders = [folder subfolders]; + for (i = 0 ; i < [subfolders count] ; i++) { + folderName = [NSString stringWithFormat:@"%@/%@", [folder nameInContainer], [subfolders objectAtIndex: i]]; + newFolder = [[SOGoMailFolder alloc] initWithName: folderName inContainer: [folder container]]; + [self cleanFolderWithFolder: newFolder + withQualifier: qualifier + recursive: isRecursive + withTrashFolder: useTrashFolder + exception: exception + counter: counter]; + [newFolder release]; + } + } +} + +- (WOResponse *) cleanMailboxAction +{ + NSDictionary *jsonRequest, *jsonResponse; + WORequest *request; + NSString *searchString, *folderName; + EOQualifier *searchQualifier; + BOOL isRecursive, useTrashFolder; + NSException *exception; + SOGoMailFolder *folder; + SOGoMailAccount *account; + NSUInteger i, counter; + NSArray *folderNames; + + request = [[self context] request]; + jsonRequest = [[request contentAsString] objectFromJSONString]; + + if ([[SOGoSystemDefaults sharedSystemDefaults] disableMailCleaning]) + return [self responseWithStatus: 401]; + + if (![self isDateStringValid: [jsonRequest objectForKey: @"date"]]) { + [self errorWithFormat: @"Error while cleaning mailbox, invalid date : %@", [jsonRequest objectForKey: @"date"]]; + return [self responseWithStatus: 502]; + } + + searchString = [NSString stringWithFormat: @"(NOT (flags = 'deleted') AND (DATE <= (NSCalendarDate)\"%@\"))", [jsonRequest objectForKey: @"date"]]; + searchQualifier = [EOQualifier qualifierWithQualifierFormat: searchString]; + isRecursive = [[jsonRequest objectForKey: @"applyToSubfolders"] boolValue]; + useTrashFolder = ![[jsonRequest objectForKey: @"permanentlyDelete"] boolValue]; + counter = 0; + exception = nil; + + if ([[self clientObject] isKindOfClass: [SOGoMailFolder class]]) { + folder = [self clientObject]; + [self cleanFolderWithFolder: folder + withQualifier: searchQualifier + recursive: isRecursive + withTrashFolder: useTrashFolder + exception: &exception + counter: &counter]; + } else if ([[self clientObject] isKindOfClass: [SOGoMailAccount class]]) { + account = [self clientObject]; + folderNames = [account allFolderPaths: SOGoMailStandardListing onlyRoot: YES]; + for (i = 0 ; i < [folderNames count] ; i ++) { + folderName = [folderNames objectAtIndex: i]; + if ([folderName hasPrefix:@"/"]) { + folderName = [folderName substringFromIndex:1]; + } + folder = [account folderWithTraversal: folderName andClassName: nil]; + // Disable clean for trash folder + if (![folderName isEqualToString: [account trashFolderNameInContext: [self context]]]) + [self cleanFolderWithFolder: folder + withQualifier: searchQualifier + recursive: isRecursive + withTrashFolder: useTrashFolder + exception: &exception + counter: &counter]; + } + + [[account imap4Connection] flushMailCaches]; + } + + if (exception) { + return [self responseWithStatus: 500]; + } + + jsonResponse = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: counter] + forKey: @"nbMessageDeleted"]; + return [self responseWithStatus: 200 andJSONRepresentation: jsonResponse]; } @end diff --git a/UI/MailerUI/UIxMailMainFrame.m b/UI/MailerUI/UIxMailMainFrame.m index 82dea706c..cb0d357f9 100644 --- a/UI/MailerUI/UIxMailMainFrame.m +++ b/UI/MailerUI/UIxMailMainFrame.m @@ -611,6 +611,17 @@ return result; } +- (BOOL) isCleanMailboxEnabled { + BOOL result; + SOGoSystemDefaults *sd; + + sd = [SOGoSystemDefaults sharedSystemDefaults]; + + result = ![sd disableMailCleaning]; + + return result; +} + @end /* UIxMailMainFrame */ @interface UIxMailFolderTemplate : UIxComponent diff --git a/UI/MailerUI/product.plist b/UI/MailerUI/product.plist index 278fb3d33..e81dec6f5 100644 --- a/UI/MailerUI/product.plist +++ b/UI/MailerUI/product.plist @@ -422,6 +422,11 @@ protectedBy = "View"; pageName = "UIxMailUserDelegationEditor"; }; + cleanMailbox = { + protectedBy = "View"; + actionClass = "UIxMailFolderActions"; + actionName = "cleanMailbox"; + }; addDelegate = { protectedBy = "View"; actionClass = "UIxMailAccountActions"; diff --git a/UI/Templates/MailerUI/UIxMailFolderTemplate.wox b/UI/Templates/MailerUI/UIxMailFolderTemplate.wox index 6fdb4f601..e0fcaf264 100644 --- a/UI/Templates/MailerUI/UIxMailFolderTemplate.wox +++ b/UI/Templates/MailerUI/UIxMailFolderTemplate.wox @@ -633,12 +633,18 @@ -