diff options
author | stuconnolly <stuart02@gmail.com> | 2010-05-24 18:07:43 +0000 |
---|---|---|
committer | stuconnolly <stuart02@gmail.com> | 2010-05-24 18:07:43 +0000 |
commit | bbe0f861dd4e3ab99aa3d555d3fc5db5ee5ae39d (patch) | |
tree | 1cf7d41f091854e8e2288946684267ce0f8ceaf4 /Source/SPExportInitializer.m | |
parent | d48005bd9b34f2fb1afd31f7487b7bbf8b9b978f (diff) | |
download | sequelpro-bbe0f861dd4e3ab99aa3d555d3fc5db5ee5ae39d.tar.gz sequelpro-bbe0f861dd4e3ab99aa3d555d3fc5db5ee5ae39d.tar.bz2 sequelpro-bbe0f861dd4e3ab99aa3d555d3fc5db5ee5ae39d.zip |
Merge export redesign branch back into trunk.
Includes a completely redesign approach to all export data types based on the use of NSOperation subclasses. CSV, SQL, XML and dot export types are currently functional, while the source files for PDF and HTML export types exist they are to be implemented, but are currently hidden from the interface.
Also includes the following:
- Completely redesigned export interface.
- The ability to customize CSV NULL values.
- The ability to specify whether the UTF-8 BOM should be used in SQL dumps.
- The ability to specify whether BLOB fields are output as hex or plain text during SQL dumps. Defaults to hex.
- Exporting currently selected tables via the tables list context menu.
Outstanding issues:
- Not all progress indicators for all export types are functional (or functioning correctly).
- A few issues related to the introduction of only exporting the content and create and drop syntax of specific tables during SQL dumps.
Needs some serious testing and benchmarking to ensure it replicates the current export functionality.
Diffstat (limited to 'Source/SPExportInitializer.m')
-rw-r--r-- | Source/SPExportInitializer.m | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/Source/SPExportInitializer.m b/Source/SPExportInitializer.m new file mode 100644 index 00000000..0ceccbd6 --- /dev/null +++ b/Source/SPExportInitializer.m @@ -0,0 +1,626 @@ +// +// $Id$ +// +// SPExporterInitializer.m +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on March 31, 2010 +// Copyright (c) 2010 Stuart Connolly. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import <MCPKit/MCPKit.h> + +#import "SPExportInitializer.h" +#import "SPStringAdditions.h" +#import "SPTableData.h" +#import "TableDocument.h" +#import "TablesList.h" +#import "SPGrowlController.h" +#import "SPMainThreadTrampoline.h" +#import "TableDocument.h" +#import "CustomQuery.h" +#import "SPFileHandle.h" +#import "SPAlertSheets.h" + +#import "SPCSVExporter.h" +#import "SPSQLExporter.h" +#import "SPXMLExporter.h" +#import "SPDotExporter.h" + +@implementation SPExportController (SPExportInitializer) + +/** + * Initializes the export process by analysing the selected criteria. + */ +- (void)initializeExportUsingSelectedOptions +{ + NSArray *dataArray = nil; + + // Get rid of the cached connection encoding + if (sqlPreviousConnectionEncoding) [sqlPreviousConnectionEncoding release], sqlPreviousConnectionEncoding = nil; + + createCustomFilename = ([exportCustomFilenameButton state] && (![[exportCustomFilenameTokenField stringValue] isEqualToString:@""])); + + // First determine what type of export the user selected + for (NSToolbarItem *item in [exportToolbar items]) + { + if ([[item itemIdentifier] isEqualToString:[exportToolbar selectedItemIdentifier]]) { + exportType = [item tag]; + break; + } + } + + // Determine what data to use (filtered result, custom query result or selected table(s)) for the export operation + exportSource = (exportType == SPDotExport) ? SPTableExport : ([exportInputMatrix selectedRow] + 1); + + NSMutableArray *exportTables = [NSMutableArray array]; + + // Set whether or not we are to export to multiple files + [self setExportToMultipleFiles:[exportFilePerTableCheck state]]; + + // Get the data depending on the source + switch (exportSource) + { + case SPFilteredExport: + dataArray = [tableContentInstance currentResult]; + break; + case SPQueryExport: + dataArray = [customQueryInstance currentResult]; + break; + case SPTableExport: + // Create an array of tables to export + for (NSMutableArray *table in tables) + { + if (exportType == SPSQLExport) { + if ([[table objectAtIndex:1] boolValue] || [[table objectAtIndex:2] boolValue] || [[table objectAtIndex:3] boolValue]) { + [exportTables addObject:table]; + } + } + else if (exportType == SPDotExport) { + [exportTables addObject:[table objectAtIndex:0]]; + } + else { + if ([[table objectAtIndex:2] boolValue]) { + [exportTables addObject:[table objectAtIndex:0]]; + } + } + } + + break; + } + + // Set the export type label + switch (exportType) + { + case SPSQLExport: + exportTypeLabel = @"SQL"; + break; + case SPCSVExport: + exportTypeLabel = @"CSV"; + break; + case SPXMLExport: + exportTypeLabel = @"XML"; + break; + case SPDotExport: + exportTypeLabel = @"Dot"; + break; + } + + // Begin the export based on the source + switch (exportSource) + { + case SPFilteredExport: + case SPQueryExport: + [self exportTables:nil orDataArray:dataArray]; + break; + case SPTableExport: + [self exportTables:exportTables orDataArray:nil]; + break; + } +} + +/** + * Exports the contents of the supplied array of tables or data array. + */ +- (void)exportTables:(NSArray *)exportTables orDataArray:(NSArray *)dataArray +{ + NSUInteger i; + SPFileHandle *singleFileHandle = nil; + BOOL singleFileHeaderHasBeenWritten = NO; + + // Change query logging mode + [tableDocumentInstance setQueryMode:SPImportExportQueryMode]; + + // Start the notification timer to allow notifications to be shown even if frontmost for long queries + [[SPGrowlController sharedGrowlController] setVisibilityForNotificationName:@"Export Finished"]; + + // Reset the interface + [[exportProgressTitle onMainThread] setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Exporting %@", @"text showing that the application is importing a supplied format"), exportTypeLabel]]; + [[exportProgressText onMainThread] setStringValue:NSLocalizedString(@"Writing...", @"text showing that app is writing text file")]; + + [[exportProgressText onMainThread] displayIfNeeded]; + [[exportProgressIndicator onMainThread] setDoubleValue:0]; + [[exportProgressIndicator onMainThread] displayIfNeeded]; + + // Open the progress sheet + [NSApp beginSheet:exportProgressWindow + modalForWindow:[tableDocumentInstance parentWindow] + modalDelegate:self + didEndSelector:nil + contextInfo:nil]; + + // CSV export + if (exportType == SPCSVExport) { + + SPCSVExporter *csvExporter = nil; + + // If the user has selected to only export to a single file or this is a filtered or custom query + // export, create the single file now and assign it to all subsequently created exporters. + if ((![self exportToMultipleFiles]) || (exportSource == SPFilteredExport) || (exportSource == SPQueryExport)) { + + NSString *filename = @""; + + // Create custom filename if required + if (createCustomFilename) { + filename = [self expandCustomFilenameFormatFromString:[exportCustomFilenameTokenField stringValue] usingTableName:nil]; + } + else { + // Determine what the file name should be + switch (exportSource) + { + case SPFilteredExport: + filename = [NSString stringWithFormat:@"%@_view", [tableDocumentInstance table]]; + break; + case SPQueryExport: + filename = @"query_result"; + break; + case SPTableExport: + filename = [tableDocumentInstance database]; + break; + } + } + + singleFileHandle = [self getFileHandleForFilePath:[[exportPathField stringValue] stringByAppendingPathComponent:filename]]; + } + + // Start the export process depending on the data source + if (exportSource == SPTableExport) { + + // Cache the number of tables being exported + exportTableCount = [exportTables count]; + + // Loop through the tables, creating an exporter for each + for (NSString *table in exportTables) + { + csvExporter = [self initializeCSVExporterForTable:table orDataArray:nil]; + + // If required create a single file handle for all CSV exports + if (![self exportToMultipleFiles]) { + [csvExporter setExportOutputFileHandle:singleFileHandle]; + + if (!singleFileHeaderHasBeenWritten) { + + NSMutableString *lineEnding = [NSMutableString stringWithString:[exportCSVLinesTerminatedField stringValue]]; + + // Escape tabs, line endings and carriage returns + [lineEnding replaceOccurrencesOfString:@"\\t" withString:@"\t" + options:NSLiteralSearch + range:NSMakeRange(0, [lineEnding length])]; + + + [lineEnding replaceOccurrencesOfString:@"\\n" withString:@"\n" + options:NSLiteralSearch + range:NSMakeRange(0, [lineEnding length])]; + + [lineEnding replaceOccurrencesOfString:@"\\r" withString:@"\r" + options:NSLiteralSearch + range:NSMakeRange(0, [lineEnding length])]; + + // Write the file header and the first table name + [singleFileHandle writeData:[[NSMutableString stringWithFormat:@"%@: %@ %@: %@ %@: %@%@%@%@ %@%@%@", + NSLocalizedString(@"Host", @"csv export host heading"), + [tableDocumentInstance host], + NSLocalizedString(@"Database", @"csv export database heading"), + [tableDocumentInstance database], + NSLocalizedString(@"Generation Time", @"csv export generation time heading"), + [NSDate date], + lineEnding, + lineEnding, + NSLocalizedString(@"Table", @"csv export table heading"), + table, + lineEnding, + lineEnding] dataUsingEncoding:[connection encoding]]]; + + singleFileHeaderHasBeenWritten = YES; + } + } + + [exporters addObject:csvExporter]; + } + } + else { + csvExporter = [self initializeCSVExporterForTable:nil orDataArray:dataArray]; + + [csvExporter setExportOutputFileHandle:singleFileHandle]; + + [exporters addObject:csvExporter]; + } + } + // SQL export + else if (exportType == SPSQLExport) { + + // Cache the number of tables being exported + exportTableCount = [exportTables count]; + + SPSQLExporter *sqlExporter = [[SPSQLExporter alloc] initWithDelegate:self]; + + [sqlExporter setSqlDatabaseHost:[tableDocumentInstance host]]; + [sqlExporter setSqlDatabaseName:[tableDocumentInstance database]]; + [sqlExporter setSqlDatabaseVersion:[tableDocumentInstance mySQLVersion]]; + + [sqlExporter setSqlOutputIncludeUTF8BOM:[exportUseUTF8BOMButton state]]; + [sqlExporter setSqlOutputEncodeBLOBasHex:[exportSQLBLOBFieldsAsHexCheck state]]; + [sqlExporter setSqlOutputCompressFile:[exportCompressOutputFile state]]; + [sqlExporter setSqlOutputIncludeErrors:[exportSQLIncludeErrorsCheck state]]; + + // Set generic properties + [sqlExporter setConnection:connection]; + [sqlExporter setExportOutputEncoding:[connection encoding]]; + [sqlExporter setExportUsingLowMemoryBlockingStreaming:[exportProcessLowMemoryButton state]]; + + // Cache the current connection encoding then change it to UTF-8 to allow SQL dumps to work + sqlPreviousConnectionEncoding = [[NSString alloc] initWithString:[tableDocumentInstance connectionEncoding]]; + sqlPreviousConnectionEncodingViaLatin1 = [tableDocumentInstance connectionEncodingViaLatin1:nil]; + + [tableDocumentInstance setConnectionEncoding:@"utf8" reloadingViews:NO]; + + NSMutableArray *tableTypes = [[NSMutableArray alloc] init]; + NSMutableDictionary *infoDict = [[NSMutableDictionary alloc] init]; + + // Build the table information dictionary as well as the table array with item type + for (NSArray *table in exportTables) + { + [infoDict setObject:[tableDataInstance informationForTable:[table objectAtIndex:0]] forKey:[table objectAtIndex:0]]; + } + + [sqlExporter setSqlTableInformation:infoDict]; + [sqlExporter setSqlExportTables:exportTables]; + + // Set the exporter's max progress + [sqlExporter setExportMaxProgress:((NSInteger)[exportProgressIndicator bounds].size.width)]; + + // Set the progress bar's max value + [exportProgressIndicator setMaxValue:[sqlExporter exportMaxProgress]]; + + [infoDict release]; + [tableTypes release]; + + NSString *filename = @""; + + // Create custom filename if required + filename = (createCustomFilename) ? [self expandCustomFilenameFormatFromString:[exportCustomFilenameTokenField stringValue] usingTableName:nil] : [NSString stringWithFormat:@"%@_%@", [tableDocumentInstance database], [[NSDate date] descriptionWithCalendarFormat:@"%Y-%m-%d" timeZone:nil locale:nil]]; + + SPFileHandle *fileHandle = [self getFileHandleForFilePath:[[exportPathField stringValue] stringByAppendingPathComponent:[filename stringByAppendingPathExtension:([exportCompressOutputFile state]) ? @"gz" : @"sql"]]]; + + [sqlExporter setExportOutputFileHandle:fileHandle]; + + [exporters addObject:sqlExporter]; + + [sqlExporter release]; + } + // XML export + else if (exportType == SPXMLExport) { + + SPXMLExporter *xmlExporter = nil; + + // If the user has selected to only export to a single file or this is a filtered or custom query + // export, create the single file now and assign it to all subsequently created exporters. + if ((![self exportToMultipleFiles]) || (exportSource == SPFilteredExport) || (exportSource == SPQueryExport)) { + + NSString *filename = @""; + + // Create custom filename if required + if (createCustomFilename) { + filename = [self expandCustomFilenameFormatFromString:[exportCustomFilenameTokenField stringValue] usingTableName:nil]; + } + else { + // Determine what the file name should be + switch (exportSource) + { + case SPFilteredExport: + filename = [NSString stringWithFormat:@"%@_view", [tableDocumentInstance table]]; + break; + case SPQueryExport: + filename = @"query_result"; + break; + case SPTableExport: + filename = [tableDocumentInstance database]; + break; + } + } + + singleFileHandle = [self getFileHandleForFilePath:[[exportPathField stringValue] stringByAppendingPathComponent:[filename stringByAppendingPathExtension:@"xml"]]]; + + // Write the file header + [self writeXMLHeaderToFileHandle:singleFileHandle]; + } + + // Start the export process depending on the data source + if (exportSource == SPTableExport) { + + // Cache the number of tables being exported + exportTableCount = [exportTables count]; + + // Loop through the tables, creating an exporter for each + for (NSString *table in exportTables) + { + xmlExporter = [self initializeXMLExporterForTable:table orDataArray:nil]; + + // If required create a single file handle for all XML exports + if (![self exportToMultipleFiles]) { + [xmlExporter setExportOutputFileHandle:singleFileHandle]; + + if (!singleFileHeaderHasBeenWritten) { + + // Write the file header + [self writeXMLHeaderToFileHandle:singleFileHandle]; + + singleFileHeaderHasBeenWritten = YES; + } + } + + [exporters addObject:xmlExporter]; + } + } + else { + xmlExporter = [self initializeXMLExporterForTable:nil orDataArray:dataArray]; + + [xmlExporter setExportOutputFileHandle:singleFileHandle]; + + [exporters addObject:xmlExporter]; + } + } + // Dot export + else if (exportType == SPDotExport) { + + // Cache the number of tables being exported + exportTableCount = [exportTables count]; + + SPDotExporter *dotExporter = [[SPDotExporter alloc] initWithDelegate:self]; + + [dotExporter setDotTableData:tableDataInstance]; + + [dotExporter setDotDatabaseHost:[tableDocumentInstance host]]; + [dotExporter setDotDatabaseName:[tableDocumentInstance database]]; + [dotExporter setDotDatabaseVersion:[tableDocumentInstance mySQLVersion]]; + + // Set generic properties + [dotExporter setConnection:connection]; + [dotExporter setExportOutputEncoding:[connection encoding]]; + [dotExporter setExportUsingLowMemoryBlockingStreaming:[exportProcessLowMemoryButton state]]; + + // Cache the current connection encoding then change it to UTF-8 to allow SQL dumps to work + sqlPreviousConnectionEncoding = [[NSString alloc] initWithString:[tableDocumentInstance connectionEncoding]]; + sqlPreviousConnectionEncodingViaLatin1 = [tableDocumentInstance connectionEncodingViaLatin1:nil]; + + [tableDocumentInstance setConnectionEncoding:@"utf8" reloadingViews:NO]; + + [dotExporter setDotExportTables:exportTables]; + + // Set the exporter's max progress + [dotExporter setExportMaxProgress:(NSInteger)[exportProgressIndicator bounds].size.width]; + + // Set the progress bar's max value + [exportProgressIndicator setMaxValue:[dotExporter exportMaxProgress]]; + + NSString *filename = @""; + + // Create custom filename if required + if (createCustomFilename) { + filename = [self expandCustomFilenameFormatFromString:[exportCustomFilenameTokenField stringValue] usingTableName:nil]; + } + else { + filename = [tableDocumentInstance database]; + } + + SPFileHandle *fileHandle = [self getFileHandleForFilePath:[[exportPathField stringValue] stringByAppendingPathComponent:[filename stringByAppendingPathExtension:@"dot"]]]; + + [dotExporter setExportOutputFileHandle:fileHandle]; + + [exporters addObject:dotExporter]; + + [dotExporter release]; + } + + // Add the first exporter to the operation queue + [operationQueue addOperation:[exporters objectAtIndex:0]]; + + // Remove the exporter we just added to the operation queue from our list of exporters + // so we know it's already been done. + [exporters removeObjectAtIndex:0]; +} + +/** + * Initialises a CSV exporter for the supplied table name or data array. + */ +- (SPCSVExporter *)initializeCSVExporterForTable:(NSString *)table orDataArray:(NSArray *)dataArray +{ + NSString *filename = @""; + SPFileHandle *fileHandle = nil; + + SPCSVExporter *csvExporter = [[SPCSVExporter alloc] initWithDelegate:self]; + + // Depeding on the export source, set the table name or data array + if (exportSource == SPTableExport) { + [csvExporter setCsvTableName:table]; + } + else { + [csvExporter setCsvDataArray:dataArray]; + } + + [csvExporter setCsvTableData:tableDataInstance]; + + [csvExporter setCsvOutputFieldNames:[exportCSVIncludeFieldNamesCheck state]]; + [csvExporter setCsvFieldSeparatorString:[exportCSVFieldsTerminatedField stringValue]]; + [csvExporter setCsvEnclosingCharacterString:[exportCSVFieldsWrappedField stringValue]]; + [csvExporter setCsvLineEndingString:[exportCSVLinesTerminatedField stringValue]]; + [csvExporter setCsvEscapeString:[exportCSVFieldsEscapedField stringValue]]; + [csvExporter setCsvNULLString:[exportCSVNULLValuesAsTextField stringValue]]; + + // If required create separate files + if ([self exportToMultipleFiles]) { + + if (createCustomFilename) { + + // Create custom filename based on the selected format + filename = [self expandCustomFilenameFormatFromString:[exportCustomFilenameTokenField stringValue] usingTableName:table]; + + // If the user chose to use a custom filename format and we exporting to multiple files, make + // sure the table name is included to ensure the output files are unique. + filename = ([[exportCustomFilenameTokenField stringValue] rangeOfString:@"table" options:NSLiteralSearch].location == NSNotFound) ? [filename stringByAppendingFormat:@"_%@", table] : filename; + } + else { + filename = table; + } + + fileHandle = [self getFileHandleForFilePath:[[exportPathField stringValue] stringByAppendingPathComponent:filename]]; + + [csvExporter setExportOutputFileHandle:fileHandle]; + } + + // Set generic properties + [csvExporter setConnection:connection]; + [csvExporter setExportOutputEncoding:[connection encoding]]; + [csvExporter setExportMaxProgress:((NSInteger)[exportProgressIndicator bounds].size.width)]; + [csvExporter setExportUsingLowMemoryBlockingStreaming:[exportProcessLowMemoryButton state]]; + + // Set the progress bar's max value + [exportProgressIndicator setMaxValue:[csvExporter exportMaxProgress]]; + + return [csvExporter autorelease]; +} + +/** + * Initialises a XML exporter for the supplied table name or data array. + */ +- (SPXMLExporter *)initializeXMLExporterForTable:(NSString *)table orDataArray:(NSArray *)dataArray +{ + NSString *filename = @""; + SPFileHandle *fileHandle = nil; + + SPXMLExporter *xmlExporter = [[SPXMLExporter alloc] initWithDelegate:self]; + + // Depeding on the export source, set the table name or data array + if (exportSource == SPTableExport) { + [xmlExporter setXmlTableName:table]; + } + else { + [xmlExporter setXmlDataArray:dataArray]; + } + + // Regardless of the export source, set exporter's table name as it's used in the output + // of table and table content exports. + [xmlExporter setXmlTableName:[tablesListInstance tableName]]; + + // If required create separate files + if ((exportSource == SPTableExport) && exportToMultipleFiles && (exportTableCount > 0)) { + filename = [[exportPathField stringValue] stringByAppendingPathComponent:table]; + + fileHandle = [self getFileHandleForFilePath:filename]; + + // Write the file header + [self writeXMLHeaderToFileHandle:fileHandle]; + + [xmlExporter setExportOutputFileHandle:fileHandle]; + } + + // Set generic properties + [xmlExporter setConnection:connection]; + [xmlExporter setExportOutputEncoding:[connection encoding]]; + [xmlExporter setExportMaxProgress:((NSInteger)[exportProgressIndicator bounds].size.width)]; + [xmlExporter setExportUsingLowMemoryBlockingStreaming:[exportProcessLowMemoryButton state]]; + + // Set the progress bar's max value + [exportProgressIndicator setMaxValue:[xmlExporter exportMaxProgress]]; + + return [xmlExporter autorelease]; +} + +/** + * Writes the XML file header to the supplied file handle. + */ +- (void)writeXMLHeaderToFileHandle:(SPFileHandle *)fileHandle +{ + NSMutableString *header = [NSMutableString string]; + + [header setString:@"<?xml version=\"1.0\"?>\n\n"]; + [header appendString:@"<!--\n-\n"]; + [header appendString:@"- Sequel Pro XML dump\n"]; + [header appendString:[NSString stringWithFormat:@"- Version %@\n-\n", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; + [header appendString:[NSString stringWithFormat:@"- %@\n- %@\n-\n", SPHomePageURL, SPDevURL]]; + [header appendString:[NSString stringWithFormat:@"- Host: %@ (MySQL %@)\n", [tableDocumentInstance host], [tableDocumentInstance mySQLVersion]]]; + [header appendString:[NSString stringWithFormat:@"- Database: %@\n", [tableDocumentInstance database]]]; + [header appendString:[NSString stringWithFormat:@"- Generation Time: %@\n", [NSDate date]]]; + [header appendString:@"-\n-->\n\n"]; + + if (exportSource == SPTableExport) { + [header appendString:[NSString stringWithFormat:@"<%@>\n\n", [[tableDocumentInstance database] HTMLEscapeString]]]; + } + + [fileHandle writeData:[header dataUsingEncoding:NSUTF8StringEncoding]]; +} + +/** + * Returns a file handle for writing at the supplied path. + */ +- (SPFileHandle *)getFileHandleForFilePath:(NSString *)filePath +{ + SPFileHandle *fileHandle = nil; + NSFileManager *fileManager = [NSFileManager defaultManager]; + + if ([fileManager fileExistsAtPath:filePath]) { + if ((![fileManager isWritableFileAtPath:filePath]) || (!(fileHandle = [SPFileHandle fileHandleForWritingAtPath:filePath]))) { + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, + NSLocalizedString(@"Couldn't replace the file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be replaced")); + return nil; + } + } + // Otherwise attempt to create a file + else { + if (![fileManager createFileAtPath:filePath contents:[NSData data] attributes:nil]) { + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, + NSLocalizedString(@"Couldn't write to file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be written")); + return nil; + } + + // Retrieve a filehandle for the file, attempting to delete it on failure. + fileHandle = [SPFileHandle fileHandleForWritingAtPath:filePath]; + + if (!fileHandle) { + [[NSFileManager defaultManager] removeFileAtPath:filePath handler:nil]; + + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, + NSLocalizedString(@"Couldn't write to file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be written")); + return nil; + } + } + + return fileHandle; +} + +@end |