Monotone-Parent: fd483b959e9ace05c008817b99952497b2b47141
Monotone-Revision: b0c0f20c06bd2f8385a77d9c2d072d367d86789a Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2009-06-03T14:33:36 Monotone-Branch: ca.inverse.sogomaint-2.0.2
parent
72a7d598d5
commit
7bda2acefd
|
@ -13,6 +13,8 @@ SOGO_REMOVEDOUBLES = sogo-contacts-removedoubles
|
||||||
$(SOGO_REMOVEDOUBLES)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS)
|
$(SOGO_REMOVEDOUBLES)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS)
|
||||||
$(SOGO_REMOVEDOUBLES)_OBJC_FILES += \
|
$(SOGO_REMOVEDOUBLES)_OBJC_FILES += \
|
||||||
sogo-contacts-removedoubles.m
|
sogo-contacts-removedoubles.m
|
||||||
|
$(SOGO_REMOVEDOUBLES)_LIB_DIRS = \
|
||||||
|
-L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ -lNGCards
|
||||||
|
|
||||||
TOOL_NAME = $(SOGO_CHECKDOUBLES) $(SOGO_REMOVEDOUBLES)
|
TOOL_NAME = $(SOGO_CHECKDOUBLES) $(SOGO_REMOVEDOUBLES)
|
||||||
|
|
||||||
|
|
|
@ -42,10 +42,10 @@ typedef void (*NSUserDefaultsInitFunction) ();
|
||||||
|
|
||||||
static unsigned int ContactsCountWarningLimit = 1000;
|
static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
|
|
||||||
static void
|
// static void
|
||||||
NSLogInhibitor (NSString *message)
|
// NSLogInhibitor (NSString *message)
|
||||||
{
|
// {
|
||||||
}
|
// }
|
||||||
|
|
||||||
@interface SOGoDoublesChecker : NSObject
|
@interface SOGoDoublesChecker : NSObject
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ NSLogInhibitor (NSString *message)
|
||||||
NSUserDefaults *ud;
|
NSUserDefaults *ud;
|
||||||
NSNumber *warningLimit;
|
NSNumber *warningLimit;
|
||||||
|
|
||||||
_NSLog_printf_handler = NSLogInhibitor;
|
// _NSLog_printf_handler = NSLogInhibitor;
|
||||||
|
|
||||||
ud = [NSUserDefaults standardUserDefaults];
|
ud = [NSUserDefaults standardUserDefaults];
|
||||||
[ud addSuiteNamed: @"sogod"];
|
[ud addSuiteNamed: @"sogod"];
|
||||||
|
@ -119,10 +119,11 @@ NSLogInhibitor (NSString *message)
|
||||||
channel = [cm acquireOpenChannelForURL: [fom folderInfoLocation]];
|
channel = [cm acquireOpenChannelForURL: [fom folderInfoLocation]];
|
||||||
if (channel)
|
if (channel)
|
||||||
{
|
{
|
||||||
sqlString = [NSString stringWithFormat: @"SELECT c_path, c_path2, c_foldername"
|
sqlString
|
||||||
@" FROM %@"
|
= [NSString stringWithFormat: @"SELECT c_path, c_path2, c_foldername"
|
||||||
@" WHERE c_folder_type = 'Contact'",
|
@" FROM %@"
|
||||||
[fom folderInfoTableName]];
|
@" WHERE c_folder_type = 'Contact'",
|
||||||
|
[fom folderInfoTableName]];
|
||||||
ex = [channel evaluateExpressionX: sqlString];
|
ex = [channel evaluateExpressionX: sqlString];
|
||||||
if (ex)
|
if (ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,6 +34,10 @@
|
||||||
#import <Foundation/NSUserDefaults.h>
|
#import <Foundation/NSUserDefaults.h>
|
||||||
#import <Foundation/NSValue.h>
|
#import <Foundation/NSValue.h>
|
||||||
|
|
||||||
|
#import <NGCards/NGVList.h>
|
||||||
|
|
||||||
|
#import <EOControl/EOQualifier.h>
|
||||||
|
|
||||||
#import <GDLAccess/EOAdaptorChannel.h>
|
#import <GDLAccess/EOAdaptorChannel.h>
|
||||||
#import <GDLAccess/EOAdaptorContext.h>
|
#import <GDLAccess/EOAdaptorContext.h>
|
||||||
|
|
||||||
|
@ -43,7 +47,31 @@
|
||||||
|
|
||||||
typedef void (*NSUserDefaultsInitFunction) ();
|
typedef void (*NSUserDefaultsInitFunction) ();
|
||||||
|
|
||||||
static unsigned int ContactsCountWarningLimit = 1000;
|
@interface NGVList (RemoveDoubles)
|
||||||
|
|
||||||
|
- (NSArray *) cardNames;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation NGVList (RemoveDoubles)
|
||||||
|
|
||||||
|
- (NSArray *) cardNames
|
||||||
|
{
|
||||||
|
NSEnumerator *cardReferences;
|
||||||
|
NSMutableArray *cardNames;
|
||||||
|
CardElement *currentReference;
|
||||||
|
|
||||||
|
cardNames = [NSMutableArray array];
|
||||||
|
|
||||||
|
cardReferences = [[self cardReferences] objectEnumerator];
|
||||||
|
|
||||||
|
while ((currentReference = [cardReferences nextObject]))
|
||||||
|
[cardNames addObject: [currentReference value: 0]];
|
||||||
|
|
||||||
|
return cardNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@interface SOGoDoublesRemover : NSObject
|
@interface SOGoDoublesRemover : NSObject
|
||||||
@end
|
@end
|
||||||
|
@ -53,16 +81,9 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
+ (void) initialize
|
+ (void) initialize
|
||||||
{
|
{
|
||||||
NSUserDefaults *ud;
|
NSUserDefaults *ud;
|
||||||
NSNumber *warningLimit;
|
|
||||||
|
|
||||||
ud = [NSUserDefaults standardUserDefaults];
|
ud = [NSUserDefaults standardUserDefaults];
|
||||||
[ud addSuiteNamed: @"sogod"];
|
[ud addSuiteNamed: @"sogod"];
|
||||||
warningLimit = [ud objectForKey: @"SOGoContactsCountWarningLimit"];
|
|
||||||
if (warningLimit)
|
|
||||||
ContactsCountWarningLimit = [warningLimit unsignedIntValue];
|
|
||||||
|
|
||||||
NSLog (@"The warning limit for folder records is set at %u",
|
|
||||||
ContactsCountWarningLimit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) feedDoubleEmails: (NSMutableDictionary *) doubleEmails
|
- (void) feedDoubleEmails: (NSMutableDictionary *) doubleEmails
|
||||||
|
@ -92,10 +113,8 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
|
|
||||||
keys = [[doubleEmails allKeys] objectEnumerator];
|
keys = [[doubleEmails allKeys] objectEnumerator];
|
||||||
while ((currentKey = [keys nextObject]))
|
while ((currentKey = [keys nextObject]))
|
||||||
{
|
if ([[doubleEmails objectForKey: currentKey] count] < 2)
|
||||||
if ([[doubleEmails objectForKey: currentKey] count] < 2)
|
[doubleEmails removeObjectForKey: currentKey];
|
||||||
[doubleEmails removeObjectForKey: currentKey];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *) detectDoubleEmailsFromRecords: (NSArray *) records
|
- (NSDictionary *) detectDoubleEmailsFromRecords: (NSArray *) records
|
||||||
|
@ -113,6 +132,32 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
return doubleEmails;
|
return doubleEmails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSArray *) fetchCardsInListsFromFolder: (GCSFolder *) folder
|
||||||
|
{
|
||||||
|
EOQualifier *qualifier;
|
||||||
|
NSMutableArray *cardsInLists;
|
||||||
|
NSDictionary *currentRecord;
|
||||||
|
NSArray *records;
|
||||||
|
NSEnumerator *recordsEnum;
|
||||||
|
NGVList *list;
|
||||||
|
|
||||||
|
cardsInLists = [NSMutableArray array];
|
||||||
|
|
||||||
|
qualifier = [EOQualifier qualifierWithQualifierFormat: @"c_component = %@",
|
||||||
|
@"vlist"];
|
||||||
|
records = [folder fetchFields: [NSArray arrayWithObject: @"c_content"]
|
||||||
|
matchingQualifier: qualifier];
|
||||||
|
recordsEnum = [records objectEnumerator];
|
||||||
|
while ((currentRecord = [recordsEnum nextObject]))
|
||||||
|
{
|
||||||
|
list = [NGVList parseSingleFromSource:
|
||||||
|
[currentRecord objectForKey: @"c_content"]];
|
||||||
|
[cardsInLists addObjectsFromArray: [list cardNames]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return cardsInLists;
|
||||||
|
}
|
||||||
|
|
||||||
- (void) removeRecord: (NSString *) recordName
|
- (void) removeRecord: (NSString *) recordName
|
||||||
fromTable: (NSString *) tableName
|
fromTable: (NSString *) tableName
|
||||||
andQuickTable: (NSString *) quickTableName
|
andQuickTable: (NSString *) quickTableName
|
||||||
|
@ -141,6 +186,8 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
NSString *tableName, *quickTableName, *currentRecordName;
|
NSString *tableName, *quickTableName, *currentRecordName;
|
||||||
NSEnumerator *recordsEnum;
|
NSEnumerator *recordsEnum;
|
||||||
|
|
||||||
|
fprintf (stderr, "Removing %d records...\n", [recordNames count]);
|
||||||
|
|
||||||
channel = [folder acquireStoreChannel];
|
channel = [folder acquireStoreChannel];
|
||||||
context = [channel adaptorContext];
|
context = [channel adaptorContext];
|
||||||
[context beginTransaction];
|
[context beginTransaction];
|
||||||
|
@ -153,7 +200,6 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
[self removeRecord: currentRecordName
|
[self removeRecord: currentRecordName
|
||||||
fromTable: tableName andQuickTable: quickTableName
|
fromTable: tableName andQuickTable: quickTableName
|
||||||
usingChannel: channel];
|
usingChannel: channel];
|
||||||
fprintf (stderr, "Removing %d records...\n", [recordNames count]);
|
|
||||||
|
|
||||||
[context commitTransaction];
|
[context commitTransaction];
|
||||||
[folder releaseChannel: channel];
|
[folder releaseChannel: channel];
|
||||||
|
@ -207,8 +253,8 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
count: max];
|
count: max];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (unsigned int) mostModifiedRecord: (NSArray *) records
|
- (int) mostModifiedRecord: (NSArray *) records
|
||||||
count: (unsigned int) max
|
count: (unsigned int) max
|
||||||
{
|
{
|
||||||
unsigned int mostModified, count, highestVersion, version;
|
unsigned int mostModified, count, highestVersion, version;
|
||||||
NSNumber *currentVersion;
|
NSNumber *currentVersion;
|
||||||
|
@ -231,11 +277,11 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
return mostModified;
|
return mostModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (unsigned int) amountOfFilledQuickFields: (NSDictionary *) record
|
- (int) amountOfFilledQuickFields: (NSDictionary *) record
|
||||||
{
|
{
|
||||||
static NSArray *quickFields = nil;
|
static NSArray *quickFields = nil;
|
||||||
id value;
|
id value;
|
||||||
unsigned int amount, count, max;
|
int amount, count, max;
|
||||||
|
|
||||||
amount = 0;
|
amount = 0;
|
||||||
|
|
||||||
|
@ -263,10 +309,10 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (unsigned int) recordWithTheMostQuickFields: (NSArray *) records
|
- (int) recordWithTheMostQuickFields: (NSArray *) records
|
||||||
count: (unsigned int) max
|
count: (unsigned int) max
|
||||||
{
|
{
|
||||||
unsigned int mostQuickFields, count, highestQFields, currentQFields;
|
int mostQuickFields, count, highestQFields, currentQFields;
|
||||||
|
|
||||||
mostQuickFields = 0;
|
mostQuickFields = 0;
|
||||||
|
|
||||||
|
@ -285,9 +331,9 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
return mostQuickFields;
|
return mostQuickFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (unsigned int) linesInContent: (NSString *) content
|
- (int) linesInContent: (NSString *) content
|
||||||
{
|
{
|
||||||
unsigned int nbrLines;
|
int nbrLines;
|
||||||
NSArray *lines;
|
NSArray *lines;
|
||||||
|
|
||||||
lines = [content componentsSeparatedByString: @"\n"];
|
lines = [content componentsSeparatedByString: @"\n"];
|
||||||
|
@ -300,10 +346,10 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
return nbrLines;
|
return nbrLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (unsigned int) mostCompleteRecord: (NSArray *) records
|
- (int) mostCompleteRecord: (NSArray *) records
|
||||||
count: (unsigned int) max
|
count: (unsigned int) max
|
||||||
{
|
{
|
||||||
unsigned int mostComplete, count, highestLines, lines;
|
int mostComplete, count, highestLines, lines;
|
||||||
NSString *content;
|
NSString *content;
|
||||||
|
|
||||||
mostComplete = 0;
|
mostComplete = 0;
|
||||||
|
@ -323,11 +369,36 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
return mostComplete;
|
return mostComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (int) record: (NSArray *) records
|
||||||
|
referencedInLists: (NSArray *) cardsInLists
|
||||||
|
{
|
||||||
|
int recordIndex, count, max;
|
||||||
|
NSDictionary *currentRecord;
|
||||||
|
|
||||||
|
recordIndex = -1;
|
||||||
|
|
||||||
|
max = [records count];
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
while (recordIndex == -1 && count < max)
|
||||||
|
{
|
||||||
|
currentRecord = [records objectAtIndex: count];
|
||||||
|
if ([cardsInLists
|
||||||
|
containsObject: [currentRecord objectForKey: @"c_name"]])
|
||||||
|
recordIndex = count;
|
||||||
|
else
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return recordIndex;
|
||||||
|
}
|
||||||
|
|
||||||
- (void) assignScores: (unsigned int *) scores
|
- (void) assignScores: (unsigned int *) scores
|
||||||
toRecords: (NSArray *) records
|
toRecords: (NSArray *) records
|
||||||
count: (unsigned int) max
|
count: (unsigned int) max
|
||||||
|
withCardsInLists: (NSArray *) cardsInLists
|
||||||
{
|
{
|
||||||
unsigned int recordIndex;
|
int recordIndex;
|
||||||
|
|
||||||
recordIndex = [self mostModifiedRecord: records count: max];
|
recordIndex = [self mostModifiedRecord: records count: max];
|
||||||
(*(scores + recordIndex))++;
|
(*(scores + recordIndex))++;
|
||||||
|
@ -335,9 +406,17 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
(*(scores + recordIndex)) += 2;
|
(*(scores + recordIndex)) += 2;
|
||||||
recordIndex = [self recordWithTheMostQuickFields: records count: max];
|
recordIndex = [self recordWithTheMostQuickFields: records count: max];
|
||||||
(*(scores + recordIndex)) += 3;
|
(*(scores + recordIndex)) += 3;
|
||||||
|
|
||||||
|
/* TODO: this method is ugly. Instead of replacing the card references in the
|
||||||
|
list with the most useful one, we remove the cards that are not
|
||||||
|
mentionned in the list. */
|
||||||
|
recordIndex = [self record: records referencedInLists: cardsInLists];
|
||||||
|
if (recordIndex > -1)
|
||||||
|
(*(scores + recordIndex)) += 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *) detectRecordsToRemove: (NSDictionary *) records
|
- (NSArray *) detectRecordsToRemove: (NSDictionary *) records
|
||||||
|
withCardsInLists: (NSArray *) cardsInLists
|
||||||
{
|
{
|
||||||
NSMutableArray *recordsToRemove;
|
NSMutableArray *recordsToRemove;
|
||||||
NSEnumerator *recordsEnum;
|
NSEnumerator *recordsEnum;
|
||||||
|
@ -346,11 +425,14 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
|
|
||||||
recordsToRemove = [NSMutableArray arrayWithCapacity: [records count] * 4];
|
recordsToRemove = [NSMutableArray arrayWithCapacity: [records count] * 4];
|
||||||
recordsEnum = [[records allValues] objectEnumerator];
|
recordsEnum = [[records allValues] objectEnumerator];
|
||||||
|
|
||||||
while ((currentRecords = [recordsEnum nextObject]))
|
while ((currentRecords = [recordsEnum nextObject]))
|
||||||
{
|
{
|
||||||
max = [currentRecords count];
|
max = [currentRecords count];
|
||||||
scores = NSZoneCalloc (NULL, max, sizeof (unsigned int));
|
scores = NSZoneCalloc (NULL, max, sizeof (unsigned int));
|
||||||
[self assignScores: scores toRecords: currentRecords count: max];
|
[self assignScores: scores
|
||||||
|
toRecords: currentRecords count: max
|
||||||
|
withCardsInLists: cardsInLists];
|
||||||
[recordsToRemove addObjectsFromArray: [self records: currentRecords
|
[recordsToRemove addObjectsFromArray: [self records: currentRecords
|
||||||
withLowestScores: scores
|
withLowestScores: scores
|
||||||
count: max]];
|
count: max]];
|
||||||
|
@ -363,22 +445,33 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
- (BOOL) removeDoublesFromFolder: (GCSFolder *) folder
|
- (BOOL) removeDoublesFromFolder: (GCSFolder *) folder
|
||||||
{
|
{
|
||||||
NSArray *fields, *records, *recordsToRemove;
|
NSArray *fields, *records, *recordsToRemove;
|
||||||
|
EOQualifier *qualifier;
|
||||||
BOOL rc;
|
BOOL rc;
|
||||||
|
|
||||||
fields = [NSArray arrayWithObjects: @"c_name", @"c_givenname", @"c_cn",
|
fields = [NSArray arrayWithObjects: @"c_name", @"c_givenname", @"c_cn",
|
||||||
@"c_sn", @"c_screenname", @"c_l", @"c_mail", @"c_o",
|
@"c_sn", @"c_screenname", @"c_l", @"c_mail", @"c_o",
|
||||||
@"c_ou", @"c_telephoneNumber", @"c_content", @"c_version",
|
@"c_ou", @"c_telephoneNumber", @"c_content", @"c_version",
|
||||||
@"c_creationdate", @"c_lastmodified", nil];
|
@"c_creationdate", @"c_lastmodified", nil];
|
||||||
records = [folder fetchFields: fields fetchSpecification: nil];
|
qualifier = [EOQualifier qualifierWithQualifierFormat: @"c_component = %@",
|
||||||
|
@"vcard"];
|
||||||
|
records = [folder fetchFields: fields matchingQualifier: qualifier];
|
||||||
|
|
||||||
if (records)
|
if (records)
|
||||||
{
|
{
|
||||||
rc = YES;
|
rc = YES;
|
||||||
recordsToRemove
|
recordsToRemove = [self detectRecordsToRemove:
|
||||||
= [self detectRecordsToRemove:
|
[self detectDoubleEmailsFromRecords: records]
|
||||||
[self detectDoubleEmailsFromRecords: records]];
|
withCardsInLists:
|
||||||
[self removeRecords: recordsToRemove fromFolder: folder];
|
[self fetchCardsInListsFromFolder: folder]];
|
||||||
fprintf (stderr, "Removed %d records from %d.\n",
|
if ([recordsToRemove count])
|
||||||
[recordsToRemove count], [records count]);
|
{
|
||||||
|
[self removeRecords: recordsToRemove fromFolder: folder];
|
||||||
|
fprintf (stderr, "Removed %d records from %d.\n",
|
||||||
|
[recordsToRemove count], [records count]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fprintf (stderr, "No record to remove. %d records kept.\n",
|
||||||
|
[records count]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -401,13 +494,12 @@ static unsigned int ContactsCountWarningLimit = 1000;
|
||||||
username, folderId];
|
username, folderId];
|
||||||
folder = [fom folderAtPath: folderPath];
|
folder = [fom folderAtPath: folderPath];
|
||||||
if (folder)
|
if (folder)
|
||||||
{
|
rc = [self removeDoublesFromFolder: folder];
|
||||||
rc = [self removeDoublesFromFolder: folder];
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf (stderr, "Folder '%s' not found.\n",
|
fprintf (stderr, "Folder '%s' of user '%s' not found.\n",
|
||||||
[folderId cStringUsingEncoding: NSUTF8StringEncoding]);
|
[folderId cStringUsingEncoding: NSUTF8StringEncoding],
|
||||||
|
[username cStringUsingEncoding: NSUTF8StringEncoding]);
|
||||||
rc = NO;
|
rc = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue