From fdef91b45a56b2f94aa477041b8f4185a2e7e7e2 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 7 Nov 2015 04:20:06 +0100 Subject: Allow import of saved export settings (in export dialog) --- Source/SPBundleEditorController.m | 2 +- Source/SPConstants.h | 10 + Source/SPConstants.m | 1 + Source/SPExportController.h | 8 + Source/SPExportController.m | 73 +++--- Source/SPExportControllerDelegate.m | 18 +- Source/SPExportFilenameUtilities.h | 1 + Source/SPExportFilenameUtilities.m | 12 + Source/SPExportSettingsPersistence.h | 20 ++ Source/SPExportSettingsPersistence.m | 427 ++++++++++++++++++++++++++++++++++- 10 files changed, 537 insertions(+), 35 deletions(-) (limited to 'Source') diff --git a/Source/SPBundleEditorController.m b/Source/SPBundleEditorController.m index 7757c0ba..9986fa98 100644 --- a/Source/SPBundleEditorController.m +++ b/Source/SPBundleEditorController.m @@ -721,7 +721,7 @@ [panel setNameFieldStringValue:[[self _currentSelectedObject] objectForKey:kBundleNameKey]]; [panel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger returnCode) { - if (returnCode != NSOKButton) return; + if (returnCode != NSFileHandlingPanelOKButton) return; // Panel is still on screen. Hide it first. (This is Apple's recommended way) [panel orderOut:nil]; diff --git a/Source/SPConstants.h b/Source/SPConstants.h index 4246906f..b236cda9 100644 --- a/Source/SPConstants.h +++ b/Source/SPConstants.h @@ -641,6 +641,16 @@ extern NSString *SPURLSchemeQueryResultStatusPathHeader; extern NSString *SPURLSchemeQueryResultMetaPathHeader; extern NSString *SPCommonCryptoExceptionName; +extern NSString *SPErrorDomain; // generic SP error domain for NSError + +typedef NS_ENUM(NSInteger,SPErrorCode) { // error codes in SPErrorDomain + /** When plist deserialization fails with nil return and no NSError or the returned object has the wrong type */ + SPErrorWrongTypeOrNil = 110001, + /** Parsed data is syntactically correct, but semantically wrong (e.g. a SPF file with the wrong content format */ + SPErrorWrongContentType = 110002, + /** Some data has a version that we don't know how to handle (can be used with e.g. SPF files, which have explicit version numbers) */ + SPErrorWrongContentVersion = 110003, +}; #define SPAppDelegate ((SPAppController *)[NSApp delegate]) diff --git a/Source/SPConstants.m b/Source/SPConstants.m index e5a407f4..4098f062 100644 --- a/Source/SPConstants.m +++ b/Source/SPConstants.m @@ -443,6 +443,7 @@ NSString *SPURLSchemeQueryResultStatusPathHeader = @"/tmp/SP_QUERY_RESULT_STAT NSString *SPURLSchemeQueryResultMetaPathHeader = @"/tmp/SP_QUERY_META_"; NSString *SPCommonCryptoExceptionName = @"SPCommonCryptoException"; +NSString *SPErrorDomain = @"SPErrorDomain"; void inline _SPClear(id *addr) { [*addr release], *addr = nil; diff --git a/Source/SPExportController.h b/Source/SPExportController.h index d36c8f81..f94596ca 100644 --- a/Source/SPExportController.h +++ b/Source/SPExportController.h @@ -254,6 +254,14 @@ - (void)openExportErrorsSheetWithString:(NSString *)errors; - (void)displayExportFinishedGrowlNotification; +/** + * Tries to set the export input to a given value or falls back to a default if not valid + * @param input The source to use + * @return YES if the source was accepted, NO otherwise + * @pre _switchTab needs to have been run before this method to decide valid inputs + */ +- (BOOL)setExportInput:(SPExportSource)input; + // IB action methods - (IBAction)export:(id)sender; - (IBAction)closeSheet:(id)sender; diff --git a/Source/SPExportController.m b/Source/SPExportController.m index 95fbc5b6..9113decc 100644 --- a/Source/SPExportController.m +++ b/Source/SPExportController.m @@ -198,9 +198,6 @@ static const NSString *SPSQLExportDropEnabled = @"SQLExportDropEnabled"; [exporters removeAllObjects]; [exportFiles removeAllObjects]; - // Select the 'selected tables' source option - [exportInputPopUpButton selectItemAtIndex:source]; - // If tables were supplied, select them if (exportTables) { @@ -231,6 +228,7 @@ static const NSString *SPSQLExportDropEnabled = @"SQLExportDropEnabled"; // Ensure interface validation [self _switchTab]; [self _updateExportAdvancedOptionsLabel]; + [self setExportInput:source]; [NSApp beginSheet:[self window] modalForWindow:[tableDocumentInstance parentWindow] @@ -306,9 +304,6 @@ static const NSString *SPSQLExportDropEnabled = @"SQLExportDropEnabled"; } [self exportTables:selectedTables asFormat:selectedExportType usingSource:selectedExportSource]; - - // Ensure UI validation - [self switchInput:exportInputPopUpButton]; } /** @@ -337,27 +332,50 @@ static const NSString *SPSQLExportDropEnabled = @"SQLExportDropEnabled"; [[sender window] orderOut:self]; } +- (BOOL)setExportInput:(SPExportSource)input +{ + SPExportSource actualInput = input; + // Dot will always be a TableExport + if(exportType == SPDotExport) { + actualInput = SPTableExport; + } + //check if the type actually is valid + else if(![[exportInputPopUpButton itemAtIndex:input] isEnabled]) { + //...no, pick a valid one instead + for (NSMenuItem *item in [exportInputPopUpButton itemArray]) { + if([item isEnabled]) { + actualInput = [exportInputPopUpButton indexOfItem:item]; + goto set_input; + } + } + // nothing found (should not happen) + SPLog(@"did not find any valid export input!?"); + return NO; + } +set_input: + exportSource = actualInput; + + [exportInputPopUpButton selectItemAtIndex:exportSource]; + + BOOL isSelectedTables = (exportSource == SPTableExport); + + [exportFilePerTableCheck setHidden:(!isSelectedTables) || (exportType == SPSQLExport)]; + [exportTableList setEnabled:isSelectedTables]; + [exportSelectAllTablesButton setEnabled:isSelectedTables]; + [exportDeselectAllTablesButton setEnabled:isSelectedTables]; + [exportRefreshTablesButton setEnabled:isSelectedTables]; + + [self updateAvailableExportFilenameTokens]; // will also update the filename itself + + return (actualInput == input); +} + /** * Enables/disables and shows/hides various interface controls depending on the selected item. */ - (IBAction)switchInput:(id)sender { - if ([sender isKindOfClass:[NSPopUpButton class]]) { - - // Determine what data to use (filtered result, custom query result or selected table(s)) for the export operation - exportSource = (exportType == SPDotExport) ? SPTableExport : [exportInputPopUpButton indexOfSelectedItem]; - - BOOL isSelectedTables = ([sender indexOfSelectedItem] == SPTableExport); - - [exportFilePerTableCheck setHidden:(!isSelectedTables) || (exportType == SPSQLExport)]; - [exportTableList setEnabled:isSelectedTables]; - [exportSelectAllTablesButton setEnabled:isSelectedTables]; - [exportDeselectAllTablesButton setEnabled:isSelectedTables]; - [exportRefreshTablesButton setEnabled:isSelectedTables]; - - [self updateAvailableExportFilenameTokens]; - [self updateDisplayedExportFilename]; - } + [self setExportInput:(SPExportSource)[exportInputPopUpButton indexOfSelectedItem]]; } /** @@ -428,7 +446,7 @@ static const NSString *SPSQLExportDropEnabled = @"SQLExportDropEnabled"; [panel setDirectoryURL:[NSURL URLWithString:[exportPathField stringValue]]]; [panel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger returnCode) { - if (returnCode == NSOKButton) { + if (returnCode == NSFileHandlingPanelOKButton) { NSString *path = [[panel directoryURL] path]; if(!path) { @throw [NSException exceptionWithName:NSInternalInconsistencyException @@ -673,9 +691,6 @@ static const NSString *SPSQLExportDropEnabled = @"SQLExportDropEnabled"; - (IBAction)exportCustomQueryResultAsFormat:(id)sender { [self exportTables:nil asFormat:[sender tag] usingSource:SPQueryExport]; - - // Ensure UI validation - [self switchInput:exportInputPopUpButton]; } #pragma mark - @@ -770,11 +785,11 @@ static const NSString *SPSQLExportDropEnabled = @"SQLExportDropEnabled"; BOOL isSQL = (exportType == SPSQLExport); BOOL isCSV = (exportType == SPCSVExport); BOOL isXML = (exportType == SPXMLExport); - BOOL isHTML = (exportType == SPHTMLExport); - BOOL isPDF = (exportType == SPPDFExport); + //BOOL isHTML = (exportType == SPHTMLExport); + //BOOL isPDF = (exportType == SPPDFExport); BOOL isDot = (exportType == SPDotExport); - BOOL enable = (isCSV || isXML || isHTML || isPDF || isDot); + BOOL enable = (isCSV || isXML /* || isHTML || isPDF */ || isDot); [exportFilePerTableCheck setHidden:(isSQL || isDot)]; [exportTableList setEnabled:(!isDot)]; diff --git a/Source/SPExportControllerDelegate.m b/Source/SPExportControllerDelegate.m index 748fec1d..cf3101cf 100644 --- a/Source/SPExportControllerDelegate.m +++ b/Source/SPExportControllerDelegate.m @@ -32,8 +32,8 @@ #import "SPExportFilenameUtilities.h" #import "SPExportFileNameTokenObject.h" -#define IS_TOKEN(x) [x isKindOfClass:[SPExportFileNameTokenObject class]] -#define IS_STRING(x) [x isKindOfClass:[NSString class]] +static inline BOOL IS_TOKEN(id x); +static inline BOOL IS_STRING(id x); // Defined to suppress warnings @interface SPExportController (SPExportControllerPrivateAPI) @@ -345,5 +345,15 @@ @end -#undef IS_TOKEN -#undef IS_STRING +#pragma mark - + +BOOL IS_TOKEN(id x) +{ + return [x isKindOfClass:[SPExportFileNameTokenObject class]]; +} + +BOOL IS_STRING(id x) +{ + return [x isKindOfClass:[NSString class]]; +} + diff --git a/Source/SPExportFilenameUtilities.h b/Source/SPExportFilenameUtilities.h index 978dfc2c..b0ba9763 100644 --- a/Source/SPExportFilenameUtilities.h +++ b/Source/SPExportFilenameUtilities.h @@ -41,6 +41,7 @@ - (void)updateDisplayedExportFilename; - (void)updateAvailableExportFilenameTokens; +- (NSArray *)currentAllowedExportFilenameTokens; - (NSString *)generateDefaultExportFilename; - (NSString *)currentDefaultExportFileExtension; - (NSString *)expandCustomFilenameFormatUsingTableName:(NSString *)table; diff --git a/Source/SPExportFilenameUtilities.m b/Source/SPExportFilenameUtilities.m index ac09dd3b..69b8f786 100644 --- a/Source/SPExportFilenameUtilities.m +++ b/Source/SPExportFilenameUtilities.m @@ -153,6 +153,18 @@ [self updateDisplayedExportFilename]; } +- (NSArray *)currentAllowedExportFilenameTokens +{ + NSArray *mixed = [exportCustomFilenameTokenPool objectValue]; + NSMutableArray *tokens = [NSMutableArray arrayWithCapacity:[mixed count]]; // ...or less + + for (id obj in mixed) { + if([obj isKindOfClass:[SPExportFileNameTokenObject class]]) [tokens addObject:obj]; + } + + return tokens; +} + /** * Generates the default export filename based on the selected export options. * diff --git a/Source/SPExportSettingsPersistence.h b/Source/SPExportSettingsPersistence.h index 90f29cfe..41609125 100644 --- a/Source/SPExportSettingsPersistence.h +++ b/Source/SPExportSettingsPersistence.h @@ -34,11 +34,31 @@ @interface SPExportController (SPExportSettingsPersistence) - (IBAction)exportCurrentSettings:(id)sender; +- (IBAction)importCurrentSettings:(id)sender; + +/** + * @return The current settings as a dictionary which can be serialized + */ - (NSDictionary *)currentSettingsAsDictionary; +/** Overwrite current export settings with those defined in dict + * @param dict The new settings to apply (passing nil is an error.) + * @param err Errors while applying (will mostly be about invalid format, type) + * Can pass NULL, if not interested in details. + * Will NOT be changed unless the method also returns NO + * @return success + */ +- (BOOL)applySettingsFromDictionary:(NSDictionary *)dict error:(NSError **)err; + /** * @return A serialized form of the "custom filename" field */ - (NSArray *)currentCustomFilenameAsArray; +/** + * @param tokenList A serialized form of the "custom filename" field + * @see currentCustomFilenameAsArray + */ +- (void)setCustomFilenameFromArray:(NSArray *)tokenList; + @end diff --git a/Source/SPExportSettingsPersistence.m b/Source/SPExportSettingsPersistence.m index 68963856..f1fe98a2 100644 --- a/Source/SPExportSettingsPersistence.m +++ b/Source/SPExportSettingsPersistence.m @@ -30,33 +30,69 @@ #import "SPExportSettingsPersistence.h" #import "SPExportFileNameTokenObject.h" +#import "SPExportFilenameUtilities.h" +#import "SPExportController+SharedPrivateAPI.h" /** * converts a ([obj state] == NSOnState) to @YES / @NO * (because doing @([obj state] == NSOnState) will result in an integer 0/1) */ static inline NSNumber *IsOn(id obj); +/** + * Sets the state of obj to NSOnState or NSOffState based on the value of ref + */ +static inline void SetOnOff(NSNumber *ref,id obj); + +static const NSString *SPFExportSettingsContentType = @"export settings"; + +@interface SPExportController (Private) + +- (void)_updateExportAdvancedOptionsLabel; +- (void)_switchTab; + +@end @interface SPExportController (SPExportSettingsPersistence_Private) +// those methods will convert the name of a C enum constant to a NSString + (NSString *)describeExportSource:(SPExportSource)es; + (NSString *)describeExportType:(SPExportType)et; + (NSString *)describeCompressionFormat:(SPFileCompressionFormat)cf; + (NSString *)describeXMLExportFormat:(SPXMLExportFormat)xf; + (NSString *)describeSQLExportInsertDivider:(SPSQLExportInsertDivider)eid; +// these will store the C enum constant named by NSString in dst and return YES, +// if a valid mapping exists. Otherwise will just return NO and not modify dst. ++ (BOOL)copyExportSourceForDescription:(NSString *)esd to:(SPExportSource *)dst; ++ (BOOL)copyCompressionFormatForDescription:(NSString *)esd to:(SPFileCompressionFormat *)dst; ++ (BOOL)copyExportTypeForDescription:(NSString *)esd to:(SPExportType *)dst; ++ (BOOL)copyXMLExportFormatForDescription:(NSString *)xfd to:(SPXMLExportFormat *)dst; ++ (BOOL)copySQLExportInsertDividerForDescription:(NSString *)xfd to:(SPSQLExportInsertDivider *)dst; + - (NSDictionary *)exporterSettings; - (NSDictionary *)csvSettings; - (NSDictionary *)dotSettings; - (NSDictionary *)xmlSettings; - (NSDictionary *)sqlSettings; +- (void)applyExporterSettings:(NSDictionary *)settings; +- (void)applyCsvSettings:(NSDictionary *)settings; +- (void)applyDotSettings:(NSDictionary *)settings; +- (void)applyXmlSettings:(NSDictionary *)settings; +- (void)applySqlSettings:(NSDictionary *)settings; + - (id)exporterSpecificSettingsForSchemaObject:(NSString *)name ofType:(SPTableType)type; - (id)dotSpecificSettingsForSchemaObject:(NSString *)name ofType:(SPTableType)type; - (id)xmlSpecificSettingsForSchemaObject:(NSString *)name ofType:(SPTableType)type; - (id)csvSpecificSettingsForSchemaObject:(NSString *)name ofType:(SPTableType)type; - (id)sqlSpecificSettingsForSchemaObject:(NSString *)name ofType:(SPTableType)type; +- (void)applyExporterSpecificSettings:(id)settings forSchemaObject:(NSString *)name ofType:(SPTableType)type; +- (void)applyDotSpecificSettings:(id)settings forSchemaObject:(NSString *)name ofType:(SPTableType)type; +- (void)applyXmlSpecificSettings:(id)settings forSchemaObject:(NSString *)name ofType:(SPTableType)type; +- (void)applyCsvSpecificSettings:(id)settings forSchemaObject:(NSString *)name ofType:(SPTableType)type; +- (void)applySqlSpecificSettings:(id)settings forSchemaObject:(NSString *)name ofType:(SPTableType)type; + @end #pragma mark - @@ -64,6 +100,7 @@ static inline NSNumber *IsOn(id obj); @implementation SPExportController (SPExportSettingsPersistence) #define NAMEOF(x) case x: return @#x +#define VALUEOF(x,y,dst) if([y isEqualToString:@#x]) { *dst = x; return YES; } + (NSString *)describeExportSource:(SPExportSource)es { @@ -75,6 +112,14 @@ static inline NSNumber *IsOn(id obj); return nil; } ++ (BOOL)copyExportSourceForDescription:(NSString *)esd to:(SPExportSource *)dst +{ + VALUEOF(SPFilteredExport, esd,dst); + VALUEOF(SPQueryExport, esd,dst); + VALUEOF(SPTableExport, esd,dst); + return NO; +} + + (NSString *)describeExportType:(SPExportType)et { switch (et) { @@ -89,6 +134,18 @@ static inline NSNumber *IsOn(id obj); return nil; } ++ (BOOL)copyExportTypeForDescription:(NSString *)etd to:(SPExportType *)dst +{ + VALUEOF(SPSQLExport, etd, dst); + VALUEOF(SPCSVExport, etd, dst); + VALUEOF(SPXMLExport, etd, dst); + VALUEOF(SPDotExport, etd, dst); + //VALUEOF(SPPDFExport, etd, dst); + //VALUEOF(SPHTMLExport, etd, dst); + //VALUEOF(SPExcelExport, etd, dst); + return NO; +} + + (NSString *)describeCompressionFormat:(SPFileCompressionFormat)cf { switch (cf) { @@ -99,6 +156,14 @@ static inline NSNumber *IsOn(id obj); return nil; } ++ (BOOL)copyCompressionFormatForDescription:(NSString *)cfd to:(SPFileCompressionFormat *)dst +{ + VALUEOF(SPNoCompression, cfd, dst); + VALUEOF(SPGzipCompression, cfd, dst); + VALUEOF(SPBzip2Compression, cfd, dst); + return NO; +} + + (NSString *)describeXMLExportFormat:(SPXMLExportFormat)xf { switch (xf) { @@ -108,6 +173,13 @@ static inline NSNumber *IsOn(id obj); return nil; } ++ (BOOL)copyXMLExportFormatForDescription:(NSString *)xfd to:(SPXMLExportFormat *)dst +{ + VALUEOF(SPXMLExportMySQLFormat, xfd, dst); + VALUEOF(SPXMLExportPlainFormat, xfd, dst); + return NO; +} + + (NSString *)describeSQLExportInsertDivider:(SPSQLExportInsertDivider)eid { switch (eid) { @@ -117,7 +189,70 @@ static inline NSNumber *IsOn(id obj); return nil; } ++ (BOOL)copySQLExportInsertDividerForDescription:(NSString *)eidd to:(SPSQLExportInsertDivider *)dst +{ + VALUEOF(SPSQLInsertEveryNDataBytes, eidd, dst); + VALUEOF(SPSQLInsertEveryNRows, eidd, dst); + return NO; +} + #undef NAMEOF +#undef VALUEOF + +- (IBAction)importCurrentSettings:(id)sender +{ + //show open file dialog + NSOpenPanel *panel = [NSOpenPanel openPanel]; + + [panel setAllowedFileTypes:@[SPFileExtensionDefault]]; + [panel setAllowsOtherFileTypes:YES]; + + [panel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) { + if(result != NSFileHandlingPanelOKButton) return; + + [panel orderOut:nil]; // Panel is still on screen. Hide it first. (This is Apple's recommended way) + + NSError *err = nil; + NSData *plist = [NSData dataWithContentsOfURL:[panel URL] + options:0 + error:&err]; + + NSDictionary *settings = nil; + if(!err) { + settings = [NSPropertyListSerialization propertyListWithData:plist + options:NSPropertyListImmutable + format:NULL + error:&err]; + } + + if(!err) { + [self applySettingsFromDictionary:settings error:&err]; + if(!err) return; + } + + // give an explanation for some errors + if([[err domain] isEqualToString:SPErrorDomain]) { + if([err code] == SPErrorWrongTypeOrNil) { + NSDictionary *info = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid file supplied!", @"export : import settings : file error title"), + NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"The selected file is either not a valid SPF file or severely corrupted.", @"export : import settings : file error description"), + }; + err = [NSError errorWithDomain:[err domain] code:[err code] userInfo:info]; + } + else if([err code] == SPErrorWrongContentType) { + NSDictionary *info = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Wrong SPF content type!", @"export : import settings : spf content type error title"), + NSLocalizedRecoverySuggestionErrorKey: [NSString stringWithFormat:NSLocalizedString(@"The selected file contains data of type “%1$@”, but type “%2$@” is needed. Please choose a different file.", @"export : import settings : spf content type error description"),[[err userInfo] objectForKey:@"isType"],[[err userInfo] objectForKey:@"expectedType"]], + }; + err = [NSError errorWithDomain:[err domain] code:[err code] userInfo:info]; + } + } + + NSAlert *alert = [NSAlert alertWithError:err]; + [alert setAlertStyle:NSCriticalAlertStyle]; + [alert runModal]; + }]; +} - (IBAction)exportCurrentSettings:(id)sender { @@ -134,6 +269,9 @@ static inline NSNumber *IsOn(id obj); [panel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger returnCode) { if(returnCode != NSFileHandlingPanelOKButton) return; + // Panel is still on screen. Hide it first. (This is Apple's recommended way) + [panel orderOut:nil]; + NSError *err = nil; NSData *plist = [NSPropertyListSerialization dataWithPropertyList:[self currentSettingsAsDictionary] format:NSPropertyListXMLFormat_v1_0 @@ -172,11 +310,42 @@ static inline NSNumber *IsOn(id obj); return tokenListOut; } +- (void)setCustomFilenameFromArray:(NSArray *)tokenList +{ + NSMutableArray *tokenListOut = [NSMutableArray arrayWithCapacity:[tokenList count]]; + NSArray *allowedTokens = [self currentAllowedExportFilenameTokens]; + + for (id obj in tokenList) { + if([obj isKindOfClass:[NSString class]]) { + [tokenListOut addObject:obj]; + } + else if([obj isKindOfClass:[NSDictionary class]]) { + //there must be at least a non-empty tokenId that is also in the token pool + NSString *tokenId = [obj objectForKey:@"tokenId"]; + if([tokenId length]) { + SPExportFileNameTokenObject *token = [SPExportFileNameTokenObject tokenWithId:tokenId]; + if([allowedTokens containsObject:token]) { + [tokenListOut addObject:token]; + continue; + } + } + SPLog(@"Ignoring an invalid or unknown token with tokenId=%@",tokenId); + } + else { + SPLog(@"unknown object in import token list: %@",obj); + } + } + + [exportCustomFilenameTokenField setObjectValue:tokenListOut]; + + [self updateDisplayedExportFilename]; +} + - (NSDictionary *)currentSettingsAsDictionary { NSMutableDictionary *root = [NSMutableDictionary dictionary]; - [root setObject:@"export settings" forKey:SPFFormatKey]; + [root setObject:SPFExportSettingsContentType forKey:SPFFormatKey]; [root setObject:@1 forKey:SPFVersionKey]; [root setObject:[exportPathField stringValue] forKey:@"exportPath"]; @@ -209,6 +378,98 @@ static inline NSNumber *IsOn(id obj); return root; } +- (BOOL)applySettingsFromDictionary:(NSDictionary *)dict error:(NSError **)err +{ + //check for dict/nil + if(![dict isKindOfClass:[NSDictionary class]]) { + if(err) { + *err = [NSError errorWithDomain:SPErrorDomain + code:SPErrorWrongTypeOrNil + userInfo:nil]; // we don't know where data came from, so we can't provide meaningful help to the user + } + return NO; + } + + //check for export settings + NSString *ctype = [dict objectForKey:SPFFormatKey]; + if (![SPFExportSettingsContentType isEqualToString:ctype]) { + if(err) { + NSDictionary *errInfo = @{ + @"isType": ctype, + @"expectedType": SPFExportSettingsContentType + }; + *err = [NSError errorWithDomain:SPErrorDomain + code:SPErrorWrongContentType + userInfo:errInfo]; + } + return NO; + } + + //check for version + NSInteger version = [[dict objectForKey:SPFVersionKey] integerValue]; + if(version != 1) { + if(err) { + NSDictionary *errInfo = @{ + @"isVersion": @(version), + NSLocalizedDescriptionKey: NSLocalizedString(@"Unsupported version for export settings!", @"export : import settings : file version error title"), + NSLocalizedRecoverySuggestionErrorKey: [NSString stringWithFormat:NSLocalizedString(@"The selected export settings were stored with version\u00A0%1$ld, but only settings with the following versions can be imported: %2$@.\n\nEither save the settings in a backwards compatible way or update your version of Sequel Pro.", @"export : import settings : file version error description ($1 = is version, $2 = list of supported versions)"),version,@"1"], + }; + *err = [NSError errorWithDomain:SPErrorDomain + code:SPErrorWrongContentVersion + userInfo:errInfo]; + } + return NO; + } + + //ok, we can try to import that... + + [exporters removeAllObjects]; + [exportFiles removeAllObjects]; + + id o; + if((o = [dict objectForKey:@"exportPath"])) [exportPathField setStringValue:o]; + + SPExportType et; + if((o = [dict objectForKey:@"exportType"]) && [[self class] copyExportTypeForDescription:o to:&et]) { + [exportTypeTabBar selectTabViewItemAtIndex:et]; + } + + //exportType should be changed first, as exportSource depends on it + SPExportSource es; + if((o = [dict objectForKey:@"exportSource"]) && [[self class] copyExportSourceForDescription:o to:&es]) { + [self setExportInput:es]; //try to set it. might fail e.g. if the settings were saved with "query result" but right now no custom query result exists + } + + // set exporter specific settings + [self applyExporterSettings:[dict objectForKey:@"settings"]]; + + // load schema object settings + if(exportSource == SPTableExport) { + NSDictionary *perObjectSettings = [dict objectForKey:@"schemaObjects"]; + + for (NSString *table in [perObjectSettings allKeys]) { + id settings = [perObjectSettings objectForKey:table]; + [self applyExporterSpecificSettings:settings forSchemaObject:table ofType:SPTableTypeTable]; + } + + [exportTableList reloadData]; + } + + if((o = [dict objectForKey:@"lowMemoryStreaming"])) [exportProcessLowMemoryButton setState:([o boolValue] ? NSOnState : NSOffState)]; + + SPFileCompressionFormat cf; + if((o = [dict objectForKey:@"compressionFormat"]) && [[self class] copyCompressionFormatForDescription:o to:&cf]) [exportOutputCompressionFormatPopupButton selectItemAtIndex:cf]; + + // might have changed + [self _updateExportAdvancedOptionsLabel]; + + // token pool is only valid once the schema object selection is done + [self updateAvailableExportFilenameTokens]; + if((o = [dict objectForKey:@"customFilename"])) [self setCustomFilenameFromArray:o]; + + return YES; +} + - (NSDictionary *)exporterSettings { switch (exportType) { @@ -230,6 +491,27 @@ static inline NSNumber *IsOn(id obj); } } +- (void)applyExporterSettings:(NSDictionary *)settings +{ + switch (exportType) { + case SPCSVExport: + return [self applyCsvSettings:settings]; + case SPSQLExport: + return [self applySqlSettings:settings]; + case SPXMLExport: + return [self applyXmlSettings:settings]; + case SPDotExport: + return [self applyDotSettings:settings]; + case SPExcelExport: + case SPHTMLExport: + case SPPDFExport: + default: + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:@"exportType not implemented!" + userInfo:@{@"exportType": @(exportType)}]; + } +} + - (NSDictionary *)csvSettings { return @{ @@ -243,11 +525,31 @@ static inline NSNumber *IsOn(id obj); }; } +- (void)applyCsvSettings:(NSDictionary *)settings +{ + id o; + if((o = [settings objectForKey:@"exportToMultipleFiles"])) SetOnOff(o,exportFilePerTableCheck); + [self toggleNewFilePerTable:nil]; + + if((o = [settings objectForKey:@"CSVIncludeFieldNames"])) SetOnOff(o, exportCSVIncludeFieldNamesCheck); + if((o = [settings objectForKey:@"CSVFieldsTerminated"])) [exportCSVFieldsTerminatedField setStringValue:o]; + if((o = [settings objectForKey:@"CSVFieldsWrapped"])) [exportCSVFieldsWrappedField setStringValue:o]; + if((o = [settings objectForKey:@"CSVLinesTerminated"])) [exportCSVLinesTerminatedField setStringValue:o]; + if((o = [settings objectForKey:@"CSVFieldsEscaped"])) [exportCSVFieldsEscapedField setStringValue:o]; + if((o = [settings objectForKey:@"CSVNULLValuesAsText"])) [exportCSVNULLValuesAsTextField setStringValue:o]; +} + - (NSDictionary *)dotSettings { return @{@"DotForceLowerTableNames": IsOn(exportDotForceLowerTableNamesCheck)}; } +- (void)applyDotSettings:(NSDictionary *)settings +{ + id o; + if((o = [settings objectForKey:@"DotForceLowerTableNames"])) SetOnOff(o, exportDotForceLowerTableNamesCheck); +} + - (NSDictionary *)xmlSettings { return @{ @@ -259,6 +561,21 @@ static inline NSNumber *IsOn(id obj); }; } +- (void)applyXmlSettings:(NSDictionary *)settings +{ + id o; + SPXMLExportFormat xmlf; + if((o = [settings objectForKey:@"exportToMultipleFiles"])) SetOnOff(o, exportFilePerTableCheck); + [self toggleNewFilePerTable:nil]; + + if((o = [settings objectForKey:@"XMLFormat"]) && [[self class] copyXMLExportFormatForDescription:o to:&xmlf]) [exportXMLFormatPopUpButton selectItemAtIndex:xmlf]; + if((o = [settings objectForKey:@"XMLOutputIncludeStructure"])) SetOnOff(o, exportXMLIncludeStructure); + if((o = [settings objectForKey:@"XMLOutputIncludeContent"])) SetOnOff(o, exportXMLIncludeContent); + if((o = [settings objectForKey:@"XMLNULLString"])) [exportXMLNULLValuesAsTextField setStringValue:o]; + + [self toggleXMLOutputFormat:exportXMLFormatPopUpButton]; +} + - (NSDictionary *)sqlSettings { BOOL includeStructure = ([exportSQLIncludeStructureCheck state] == NSOnState); @@ -267,6 +584,7 @@ static inline NSNumber *IsOn(id obj); @"SQLIncludeStructure": IsOn(exportSQLIncludeStructureCheck), @"SQLIncludeContent": IsOn(exportSQLIncludeContentCheck), @"SQLIncludeErrors": IsOn(exportSQLIncludeErrorsCheck), + @"SQLIncludeDROP": IsOn(exportSQLIncludeDropSyntaxCheck), @"SQLUseUTF8BOM": IsOn(exportUseUTF8BOMButton), @"SQLBLOBFieldsAsHex": IsOn(exportSQLBLOBFieldsAsHexCheck), @"SQLInsertNValue": @([exportSQLInsertNValueTextField integerValue]), @@ -283,6 +601,32 @@ static inline NSNumber *IsOn(id obj); return dict; } +- (void)applySqlSettings:(NSDictionary *)settings +{ + id o; + SPSQLExportInsertDivider div; + + if((o = [settings objectForKey:@"SQLIncludeContent"])) SetOnOff(o, exportSQLIncludeContentCheck); + [self toggleSQLIncludeContent:exportSQLIncludeContentCheck]; + + if((o = [settings objectForKey:@"SQLIncludeDROP"])) SetOnOff(o, exportSQLIncludeDropSyntaxCheck); + [self toggleSQLIncludeDropSyntax:exportSQLIncludeDropSyntaxCheck]; + + if((o = [settings objectForKey:@"SQLIncludeStructure"])) SetOnOff(o, exportSQLIncludeStructureCheck); + [self toggleSQLIncludeStructure:exportSQLIncludeStructureCheck]; + + if((o = [settings objectForKey:@"SQLIncludeErrors"])) SetOnOff(o, exportSQLIncludeErrorsCheck); + if((o = [settings objectForKey:@"SQLUseUTF8BOM"])) SetOnOff(o, exportUseUTF8BOMButton); + if((o = [settings objectForKey:@"SQLBLOBFieldsAsHex"])) SetOnOff(o, exportSQLBLOBFieldsAsHexCheck); + if((o = [settings objectForKey:@"SQLInsertNValue"])) [exportSQLInsertNValueTextField setIntegerValue:[o integerValue]]; + if((o = [settings objectForKey:@"SQLInsertDivider"]) && [[self class] copySQLExportInsertDividerForDescription:o to:&div]) [exportSQLInsertDividerPopUpButton selectItemAtIndex:div]; + + if([exportSQLIncludeStructureCheck state] == NSOnState) { + if((o = [settings objectForKey:@"SQLIncludeAutoIncrementValue"])) SetOnOff(o, exportSQLIncludeAutoIncrementValueButton); + if((o = [settings objectForKey:@"SQLIncludeDropSyntax"])) SetOnOff(o, exportSQLIncludeDropSyntaxCheck); + } +} + - (id)exporterSpecificSettingsForSchemaObject:(NSString *)name ofType:(SPTableType)type { switch (exportType) { @@ -304,12 +648,38 @@ static inline NSNumber *IsOn(id obj); } } +- (void)applyExporterSpecificSettings:(id)settings forSchemaObject:(NSString *)name ofType:(SPTableType)type +{ + switch (exportType) { + case SPCSVExport: + return [self applyCsvSpecificSettings:settings forSchemaObject:name ofType:type]; + case SPSQLExport: + return [self applySqlSpecificSettings:settings forSchemaObject:name ofType:type]; + case SPXMLExport: + return [self applyXmlSpecificSettings:settings forSchemaObject:name ofType:type]; + case SPDotExport: + return [self applyDotSpecificSettings:settings forSchemaObject:name ofType:type]; + case SPExcelExport: + case SPHTMLExport: + case SPPDFExport: + default: + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:@"exportType not implemented!" + userInfo:@{@"exportType": @(exportType)}]; + } +} + - (id)dotSpecificSettingsForSchemaObject:(NSString *)name ofType:(SPTableType)type { // Dot is a graph of the whole database - nothing to pick from return nil; } +- (void)applyDotSpecificSettings:(id)settings forSchemaObject:(NSString *)name ofType:(SPTableType)type +{ + //should never be called +} + - (id)xmlSpecificSettingsForSchemaObject:(NSString *)name ofType:(SPTableType)type { // XML per table setting is only yes/no @@ -324,6 +694,20 @@ static inline NSNumber *IsOn(id obj); return nil; } +- (void)applyXmlSpecificSettings:(id)settings forSchemaObject:(NSString *)name ofType:(SPTableType)type +{ + // XML per table setting is only yes/no + if(type == SPTableTypeTable) { + // we have to look through the table views' rows to find the appropriate table... + for (NSMutableArray *table in tables) { + if([[table objectAtIndex:0] isEqualTo:name]) { + [table replaceObjectAtIndex:2 withObject:@([settings boolValue])]; + return; + } + } + } +} + - (id)csvSpecificSettingsForSchemaObject:(NSString *)name ofType:(SPTableType)type { // CSV per table setting is only yes/no @@ -338,6 +722,20 @@ static inline NSNumber *IsOn(id obj); return nil; } +- (void)applyCsvSpecificSettings:(id)settings forSchemaObject:(NSString *)name ofType:(SPTableType)type +{ + // CSV per table setting is only yes/no + if(type == SPTableTypeTable) { + // we have to look through the table views' rows to find the appropriate table... + for (NSMutableArray *table in tables) { + if([[table objectAtIndex:0] isEqualTo:name]) { + [table replaceObjectAtIndex:2 withObject:@([settings boolValue])]; + return; + } + } + } +} + - (id)sqlSpecificSettingsForSchemaObject:(NSString *)name ofType:(SPTableType)type { BOOL structure = ([exportSQLIncludeStructureCheck state] == NSOnState); @@ -370,6 +768,28 @@ static inline NSNumber *IsOn(id obj); return nil; } +- (void)applySqlSpecificSettings:(id)settings forSchemaObject:(NSString *)name ofType:(SPTableType)type +{ + BOOL structure = ([exportSQLIncludeStructureCheck state] == NSOnState); + BOOL content = ([exportSQLIncludeContentCheck state] == NSOnState); + BOOL drop = ([exportSQLIncludeDropSyntaxCheck state] == NSOnState); + + // SQL allows per table setting of structure/content/drop table + if(type == SPTableTypeTable) { + // we have to look through the table views' rows to find the appropriate table... + for (NSMutableArray *table in tables) { + if([[table objectAtIndex:0] isEqualTo:name]) { + NSArray *flags = settings; + + [table replaceObjectAtIndex:1 withObject:@((structure && [flags containsObject:@"structure"]))]; + [table replaceObjectAtIndex:2 withObject:@((content && [flags containsObject:@"content"]))]; + [table replaceObjectAtIndex:3 withObject:@((drop && [flags containsObject:@"drop"]))]; + return; + } + } + } +} + @end #pragma mark - @@ -378,3 +798,8 @@ NSNumber *IsOn(id obj) { return (([obj state] == NSOnState)? @YES : @NO); } + +void SetOnOff(NSNumber *ref,id obj) +{ + [obj setState:([ref boolValue] ? NSOnState : NSOffState)]; +} -- cgit v1.2.3