aboutsummaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authormltownsend <mltownsend@gmail.com>2009-08-11 17:00:46 +0000
committermltownsend <mltownsend@gmail.com>2009-08-11 17:00:46 +0000
commit80f757d7f217ae836f6c8de60134bc2a5d005687 (patch)
tree6969e36a3bd1f63227d67d2f2bdb224c36b1aeed /Source
parentf6afd1e9699b891f1deee9058a1737e8c5744bd7 (diff)
downloadsequelpro-80f757d7f217ae836f6c8de60134bc2a5d005687.tar.gz
sequelpro-80f757d7f217ae836f6c8de60134bc2a5d005687.tar.bz2
sequelpro-80f757d7f217ae836f6c8de60134bc2a5d005687.zip
User Manager feature
Diffstat (limited to 'Source')
-rw-r--r--Source/SPArrayAdditions.h1
-rw-r--r--Source/SPArrayAdditions.m15
-rw-r--r--Source/SPStringAdditions.h2
-rw-r--r--Source/SPStringAdditions.m36
-rw-r--r--Source/SPUserMO.h41
-rw-r--r--Source/SPUserMO.m72
-rw-r--r--Source/SPUserManager.h73
-rw-r--r--Source/SPUserManager.m680
-rw-r--r--Source/SPUserManager.xcdatamodel/elementsbin0 -> 50142 bytes
-rw-r--r--Source/SPUserManager.xcdatamodel/layoutbin0 -> 5707 bytes
-rw-r--r--Source/TableDocument.h4
-rw-r--r--Source/TableDocument.m23
12 files changed, 945 insertions, 2 deletions
diff --git a/Source/SPArrayAdditions.h b/Source/SPArrayAdditions.h
index 45b81e03..473740fc 100644
--- a/Source/SPArrayAdditions.h
+++ b/Source/SPArrayAdditions.h
@@ -31,6 +31,7 @@ static inline id NSArrayObjectAtIndex(NSArray* self, NSUInteger i) {
@interface NSArray (SPArrayAdditions)
- (NSString *)componentsJoinedAndBacktickQuoted;
+- (NSString *)componentsJoinedByCommas;
- (NSArray *)subarrayWithIndexes:(NSIndexSet *)indexes;
@end
diff --git a/Source/SPArrayAdditions.m b/Source/SPArrayAdditions.m
index 853421e0..37b1ba62 100644
--- a/Source/SPArrayAdditions.m
+++ b/Source/SPArrayAdditions.m
@@ -42,6 +42,21 @@
return result;
}
+- (NSString *)componentsJoinedByCommas
+{
+ NSString *result = [NSString string];
+ for (NSString *component in self)
+ {
+ if ([result length])
+ {
+ result = [result stringByAppendingString:@","];
+ }
+
+ result = [result stringByAppendingString:component];
+ }
+ return result;
+}
+
- (NSArray *)subarrayWithIndexes:(NSIndexSet *)indexes
{
NSMutableArray *subArray = [NSMutableArray arrayWithCapacity:[indexes count]];
diff --git a/Source/SPStringAdditions.h b/Source/SPStringAdditions.h
index 764e6b24..6ee941c2 100644
--- a/Source/SPStringAdditions.h
+++ b/Source/SPStringAdditions.h
@@ -44,6 +44,8 @@ static inline const char* NSStringUTF8String(NSString* self) {
+ (NSString *)stringForTimeInterval:(float)timeInterval;
- (NSString *)backtickQuotedString;
+- (NSString *)tickQuotedString;
+- (NSString *)replaceUnderscoreWithSpace;
- (NSArray *)lineRangesForRange:(NSRange)aRange;
- (NSString *)createViewSyntaxPrettifier;
diff --git a/Source/SPStringAdditions.m b/Source/SPStringAdditions.m
index 81c501f9..5ddc14f3 100644
--- a/Source/SPStringAdditions.m
+++ b/Source/SPStringAdditions.m
@@ -163,6 +163,42 @@
return quotedString;
}
+// -------------------------------------------------------------------------------
+// tickQuotedString
+//
+// Returns the string quoted with ticks as required for MySQL identifiers
+// eg.: tablename => 'tablename'
+// my'table => 'my''table'
+// -------------------------------------------------------------------------------
+- (NSString *)tickQuotedString
+{
+ // mutableCopy automatically retains the returned string, so don't forget to release it later...
+ NSMutableString *workingCopy = [self mutableCopy];
+
+ // First double all backticks in the string to escape them
+ // I don't want to use "stringByReplacingOccurrencesOfString:withString:" because it's only available in 10.5
+ [workingCopy replaceOccurrencesOfString: @"'"
+ withString: @"''"
+ options: NSLiteralSearch
+ range: NSMakeRange(0, [workingCopy length]) ];
+
+ // Add the quotes around the string
+ NSString *quotedString = [NSString stringWithFormat: @"'%@'", workingCopy];
+
+ [workingCopy release];
+
+ return quotedString;
+}
+
+- (NSString *)replaceUnderscoreWithSpace
+{
+ NSMutableString *workingCopy = [self mutableCopy];
+ [workingCopy replaceOccurrencesOfString:@"_"
+ withString:@" "
+ options:NSLiteralSearch
+ range:NSMakeRange(0, [workingCopy length])];
+ return [workingCopy autorelease];
+}
// -------------------------------------------------------------------------------
// createViewSyntaxPrettifier
diff --git a/Source/SPUserMO.h b/Source/SPUserMO.h
new file mode 100644
index 00000000..13837921
--- /dev/null
+++ b/Source/SPUserMO.h
@@ -0,0 +1,41 @@
+//
+// $Id: SPUserMO.h 856 2009-06-12 05:31:39Z mltownsend $
+//
+// SPUserMO.h
+// sequel-pro
+//
+// Created by Mark Townsend on Jan 01, 2009
+//
+// 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 <CoreData/CoreData.h>
+
+@interface NSManagedObject (CoreDataGeneratedAccessors)
+
+@property(nonatomic, retain) NSString *user;
+@property(nonatomic, retain) NSString *host;
+@property(nonatomic, retain) NSManagedObject *parent;
+@property (nonatomic, retain) NSSet *children;
+
+- (NSString *)displayName;
+- (void)setDisplayName:(NSString *)value;
+
+// Access to-many relationship via -[NSObject mutableSetValueForKey:]
+- (void)addChildrenObject:(NSManagedObject *)value;
+- (void)removeChildrenObject:(NSManagedObject *)value;
+
+@end \ No newline at end of file
diff --git a/Source/SPUserMO.m b/Source/SPUserMO.m
new file mode 100644
index 00000000..05d699ae
--- /dev/null
+++ b/Source/SPUserMO.m
@@ -0,0 +1,72 @@
+//
+// $Id: SPUserMO.m 856 2009-06-12 05:31:39Z mltownsend $
+//
+// SPUserMO.m
+// sequel-pro
+//
+// Created by Mark Townsend on Jan 01, 2009
+//
+// 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 "SPUserMO.h"
+
+@implementation NSManagedObject (CoreDataGeneratedAccessors)
+
+@dynamic user, host;
+@dynamic parent;
+@dynamic children;
+
+- (NSString *)displayName
+{
+ if ([self valueForKey:@"parent"] == nil) {
+ return self.user;
+ } else {
+ return self.host;
+ }
+}
+
+- (void)setDisplayName:(NSString *)value
+{
+ [self setValue:value forKey:@"host"];
+}
+
+- (void)addChildrenObject:(NSManagedObject *)value
+{
+ NSSet *changedObjects = [[NSSet alloc] initWithObjects:&value count:1];
+
+ [self willChangeValueForKey:@"children" withSetMutation:NSKeyValueUnionSetMutation usingObjects:changedObjects];
+ [[self primitiveValueForKey:@"children"] addObject:value];
+ [self didChangeValueForKey:@"children" withSetMutation:NSKeyValueUnionSetMutation usingObjects:changedObjects];
+
+ [changedObjects release];
+ value.user = self.user;
+}
+
+- (void)removeChildrenObject:(NSManagedObject *)value
+{
+ NSSet *changedObjects = [[NSSet alloc] initWithObjects:&value count:1];
+
+ [self willChangeValueForKey:@"children" withSetMutation:NSKeyValueMinusSetMutation usingObjects:changedObjects];
+ [[self primitiveValueForKey:@"children"] removeObject:value];
+ [self didChangeValueForKey:@"children" withSetMutation:NSKeyValueMinusSetMutation usingObjects:changedObjects];
+
+ [changedObjects release];
+}
+@end
+
+
+
diff --git a/Source/SPUserManager.h b/Source/SPUserManager.h
new file mode 100644
index 00000000..febeb472
--- /dev/null
+++ b/Source/SPUserManager.h
@@ -0,0 +1,73 @@
+//
+// $Id: SPUserManager.h 856 2009-06-12 05:31:39Z mltownsend $
+//
+// SPUserManager.h
+// sequel-pro
+//
+// Created by Mark Townsend on Jan 01, 2009
+//
+// 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 <Cocoa/Cocoa.h>
+
+@class MCPConnection;
+
+@interface SPUserManager : NSObject {
+
+ NSPersistentStoreCoordinator *persistentStoreCoordinator;
+ NSManagedObjectModel *managedObjectModel;
+ NSManagedObjectContext *managedObjectContext;
+ NSDictionary *privColumnsMODict;
+
+ BOOL isInitializing;
+
+ MCPConnection* mySqlConnection;
+
+ IBOutlet NSOutlineView* outlineView;
+ IBOutlet NSTabView *tabView;
+ IBOutlet NSTreeController *treeController;
+ IBOutlet NSWindow *window;
+ IBOutlet NSPanel *addHostSheet;
+}
+
+- (NSPersistentStoreCoordinator *)persistentStoreCoordinator;
+- (NSManagedObjectModel *)managedObjectModel;
+- (NSManagedObjectContext *)managedObjectContext;
+
+- (id)initWithConnection:(MCPConnection *)connection;
+- (void)setConnection:(MCPConnection *)connection;
+- (MCPConnection *)connection;
+- (void)show;
+- (void)initializeChild:(NSManagedObject *)child withItem:(NSDictionary *)item;
+
+// Add/Remove Users
+- (IBAction)addUser:(id)sender;
+- (IBAction)removeUser:(id)sender;
+- (IBAction)addHost:(id)sender;
+- (IBAction)removeHost:(id)sender;
+
+// General
+- (IBAction)doCancel:(id)sender;
+- (IBAction)doApply:(id)sender;
+
+// Core Data Notifications
+- (void)contextDidSave:(NSNotification *)notification;
+- (BOOL)insertUsers:(NSArray *)insertedUsers;
+- (BOOL)deleteUsers:(NSArray *)deletedUsers;
+- (BOOL)updateUsers:(NSArray *)updatedUsers;
+- (BOOL)grantPrivilegesToUser:(NSManagedObject *)user;
+@end
diff --git a/Source/SPUserManager.m b/Source/SPUserManager.m
new file mode 100644
index 00000000..9ef3e8ba
--- /dev/null
+++ b/Source/SPUserManager.m
@@ -0,0 +1,680 @@
+//
+// $Id: SPUserManager.m 856 2009-06-12 05:31:39Z mltownsend $
+//
+// SPUserManager.m
+// sequel-pro
+//
+// Created by Mark Townsend on Jan 01, 2009
+//
+// 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 "SPUserManager.h"
+#import "MCPConnection.h"
+#import "SPUserMO.h"
+#import "MCPResult.h"
+#import "ImageAndTextCell.h"
+#import "SPArrayAdditions.h"
+#import "SPStringAdditions.h"
+
+#define COLUMNIDNAME @"NameColumn"
+
+@interface SPUserManager (PrivateMethods)
+- (void)_initializeTree:(NSArray *)items;
+- (void)_initializeUsers;
+- (void)_selectParentFromSelection;
+- (NSArray *)_fetchUserWithUserName:(NSString *)username;
+- (NSManagedObject *)_createNewSPUser;
+- (BOOL)checkAndDisplayMySqlError;
+- (void)_clearData;
+@end
+
+@implementation SPUserManager
+
+- (id)init
+{
+ [self dealloc];
+ @throw [NSException exceptionWithName:@"BadInitCall" reason:@"Can't call init here" userInfo:nil];
+ return nil;
+}
+
+- (id)initWithConnection:(MCPConnection*) connection
+{
+ if (![super init]) {
+ return nil;
+ }
+
+ [self setConnection:connection];
+
+
+ privColumnsMODict = [[[NSDictionary alloc] initWithObjectsAndKeys:
+ @"grant_option_priv",@"Grant_priv",
+ @"show_databases_priv",@"Show_db_priv",
+ @"create_temporary_table_priv",@"Create_tmp_table_priv",
+ @"Replication_slave_priv",@"Repl_slave_priv",
+ @"Replication_client_priv",@"Repl_client_priv",nil] retain];
+
+ if (!outlineView) {
+ [NSBundle loadNibNamed:@"UserManagerView" owner:self];
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ NSLog(@"SPUserManager dealloc.");
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [managedObjectContext release], managedObjectContext = nil;
+ [persistentStoreCoordinator release], persistentStoreCoordinator = nil;
+ [managedObjectModel release], managedObjectModel = nil;
+ [privColumnsMODict release], privColumnsMODict = nil;
+
+ [mySqlConnection release];
+ [super dealloc];
+}
+
+- (void)awakeFromNib
+{
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(contextDidSave:)
+ name:NSManagedObjectContextDidSaveNotification
+ object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(contextDidChange:)
+ name:NSManagedObjectContextObjectsDidChangeNotification
+ object:nil];
+ [tabView selectTabViewItemAtIndex:0];
+
+ NSTableColumn *tableColumn = [outlineView tableColumnWithIdentifier:COLUMNIDNAME];
+ ImageAndTextCell *imageAndTextCell = [[[ImageAndTextCell alloc] init] autorelease];
+
+ [imageAndTextCell setEditable:NO];
+ [tableColumn setDataCell:imageAndTextCell];
+
+ [NSThread detachNewThreadSelector:@selector(_initializeUsers) toTarget:self withObject:nil];
+}
+
+- (void)_initializeUsers
+{
+ isInitializing = TRUE;
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSMutableArray *resultAsArray = [NSMutableArray array];
+ NSMutableArray *usersResultArray = [NSMutableArray array];
+
+ [[self connection] selectDB:@"mysql"];
+ MCPResult *result = [[[self connection] queryString:@"select * from user order by user"] retain];
+ int rows = [result numOfRows];
+ if (rows > 0)
+ {
+ // Go to the beginning
+ [result dataSeek:0];
+ }
+
+ for(int i = 0; i < rows; i++)
+ {
+ [resultAsArray addObject:[result fetchRowAsDictionary]];
+ }
+
+ [usersResultArray addObjectsFromArray:resultAsArray];
+
+ [self _initializeTree:usersResultArray];
+
+ [result release];
+ [pool release];
+ isInitializing = FALSE;
+}
+
+- (void)_initializeTree:(NSArray *)items
+{
+
+ for(int i = 0; i < [items count]; i++)
+ {
+ NSString *username = [[items objectAtIndex:i] objectForKey:@"User"];
+ NSArray *array = [[self _fetchUserWithUserName:username] retain];
+ NSDictionary *item = [items objectAtIndex:i];
+
+ if (array != nil && [array count] > 0)
+ {
+ // Add Children
+ NSManagedObject *parent = [array objectAtIndex:0];
+ NSManagedObject *child = [self _createNewSPUser];
+ [child setParent:parent];
+ [parent addChildrenObject:child];
+
+ [self initializeChild:child withItem:item];
+
+ } else {
+ // Add Parent
+ NSManagedObject *parent = [self _createNewSPUser];
+ NSManagedObject *child = [self _createNewSPUser];
+
+ [parent setValue:username forKey:@"user"];
+ [parent setValue:[item objectForKey:@"Password"] forKey:@"password"];
+ [parent addChildrenObject:child];
+ [child setParent:parent];
+
+ [self initializeChild:child withItem:item];
+ }
+ // Save the initialized objects so that any new changes will be tracked.
+ NSError *error = nil;
+ [[self managedObjectContext] save:&error];
+ if (error != nil)
+ {
+ [[NSApplication sharedApplication] presentError:error];
+ }
+ [array release];
+ }
+}
+
+- (void)initializeChild:(NSManagedObject *)child withItem:(NSDictionary *)item
+{
+ for (NSString *key in item)
+ {
+ NS_DURING
+ if ([key hasSuffix:@"_priv"])
+ {
+ // Special case keys
+ if ([privColumnsMODict objectForKey:key] != nil)
+ {
+ key = [privColumnsMODict objectForKey:key];
+ }
+
+ BOOL value = [[item objectForKey:key] boolValue];
+ [child setValue:[NSNumber numberWithBool:value] forKey:key];
+ }
+ else if ([key hasPrefix:@"max"])
+ {
+ NSNumber *value = [NSNumber numberWithInt:[[item objectForKey:key] intValue]];
+ [child setValue:value forKey:key];
+ }
+ else if (![key isEqualToString:@"User"] && ![key isEqualToString:@"Password"])
+ {
+ NSString *value = [item objectForKey:key];
+ [child setValue:value forKey:key];
+ }
+ NS_HANDLER
+ NSLog(@"%@ not implemented yet.", key);
+ NS_ENDHANDLER
+ }
+
+}
+
+/**
+ Creates, retains, and returns the managed object model for the application
+ by merging all of the models found in the application bundle.
+ */
+
+- (NSManagedObjectModel *)managedObjectModel {
+
+ if (managedObjectModel != nil) {
+ return managedObjectModel;
+ }
+
+ managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
+ return managedObjectModel;
+}
+
+/**
+ Returns the persistent store coordinator for the application. This
+ implementation will create and return a coordinator, having added the
+ store for the application to it. (The folder for the store is created,
+ if necessary.)
+ */
+
+- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
+
+ if (persistentStoreCoordinator != nil) {
+ return persistentStoreCoordinator;
+ }
+
+ NSError *error;
+
+ persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
+ if (![persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:&error])
+ {
+ [[NSApplication sharedApplication] presentError:error];
+ }
+
+ return persistentStoreCoordinator;
+}
+
+/**
+ Returns the managed object context for the application (which is already
+ bound to the persistent store coordinator for the application.)
+ */
+
+- (NSManagedObjectContext *) managedObjectContext {
+
+ if (managedObjectContext != nil) {
+ return managedObjectContext;
+ }
+
+ NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
+ if (coordinator != nil) {
+ managedObjectContext = [[NSManagedObjectContext alloc] init];
+ [managedObjectContext setPersistentStoreCoordinator: coordinator];
+ }
+
+ return managedObjectContext;
+}
+
+- (void)setConnection:(MCPConnection *)connection
+{
+ [connection retain];
+ [mySqlConnection release];
+ mySqlConnection = connection;
+}
+
+- (MCPConnection* )connection
+{
+ return mySqlConnection;
+}
+
+- (void)show
+{
+// [NSThread detachNewThreadSelector:@selector(_initializeUsers) toTarget:self withObject:nil];
+ [window makeKeyAndOrderFront:nil];
+}
+
+#pragma mark -
+#pragma mark OutlineView Delegate Methods
+- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ if ([cell isKindOfClass:[ImageAndTextCell class]])
+ {
+ if ([(NSManagedObject *)[item representedObject] parent] != nil)
+ {
+ NSImage *image1 = [[NSImage imageNamed:NSImageNameNetwork] retain];
+ [image1 setScalesWhenResized:YES];
+ [image1 setSize:(NSSize){16,16}];
+ [(ImageAndTextCell*)cell setImage:image1];
+ [image1 release];
+
+ }
+ else
+ {
+ NSImage *image1 = [[NSImage imageNamed:NSImageNameUser] retain];
+ [image1 setScalesWhenResized:YES];
+ [image1 setSize:(NSSize){16,16}];
+ [(ImageAndTextCell*)cell setImage:image1];
+ [image1 release];
+ }
+ }
+}
+
+- (BOOL)outlineView:(NSOutlineView *)olv isGroupItem:(id)item
+{
+ return FALSE;
+}
+
+- (BOOL)outlineView:(NSOutlineView *)olv shouldSelectItem:(id)item
+{
+ return TRUE;
+}
+
+- (BOOL)outlineView:(NSOutlineView *)olv shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ if ([[[item representedObject] children] count] == 0)
+ {
+ return TRUE;
+ }
+ return FALSE;
+
+}
+
+- (void)outlineViewSelectionDidChange:(NSNotification *)notification
+{
+ id selectedObject = [[treeController selectedObjects] objectAtIndex:0];
+ if ([selectedObject parent] == nil && !([[[tabView selectedTabViewItem] identifier] isEqualToString:@"General"]))
+ {
+ [tabView selectTabViewItemWithIdentifier:@"General"];
+ }
+ else
+ {
+ if ([[[tabView selectedTabViewItem] identifier] isEqualToString:@"General"])
+ {
+ [tabView selectTabViewItemWithIdentifier:@"Global Privileges"];
+ }
+ }
+}
+
+// General Action Methods
+- (IBAction)doCancel:(id)sender
+{
+ [[self managedObjectContext] rollback];
+ [window close];
+}
+
+- (IBAction)doApply:(id)sender
+{
+ NSError *error = nil;
+ [[self managedObjectContext] save:&error];
+ if (error != nil)
+ {
+ [[NSApplication sharedApplication] presentError:error];
+ }
+ else
+ {
+ [window close];
+ }
+// [self _clearData];
+}
+
+- (IBAction)addUser:(id)sender
+{
+ if ([[treeController selectedObjects] count] > 0)
+ {
+ if ([[[treeController selectedObjects] objectAtIndex:0] parent] != nil)
+ {
+ [self _selectParentFromSelection];
+ }
+ }
+ NSManagedObject *newItem = [self _createNewSPUser];
+ NSManagedObject *newChild = [self _createNewSPUser];
+ [newChild setValue:@"localhost" forKey:@"host"];
+ [newItem addChildrenObject:newChild];
+
+ [treeController addObject:newItem];
+ [outlineView expandItem:[outlineView itemAtRow:[outlineView selectedRow]]];
+}
+
+- (IBAction)removeUser:(id)sender
+{
+ [treeController remove:sender];
+}
+
+- (IBAction)addHost:(id)sender
+{
+ if ([[treeController selectedObjects] count] > 0)
+ {
+ if ([[[treeController selectedObjects] objectAtIndex:0] parent] != nil)
+ {
+ [self _selectParentFromSelection];
+ }
+ }
+ [treeController addChild:sender];
+ // Need to figure out how to do this right. I want to be able to have the newly
+ // added item be in edit mode to change the host name.
+// [outlineView editColumn:0 row:[outlineView selectedRow] withEvent:nil select:TRUE];
+}
+
+- (IBAction)removeHost:(id)sender
+{
+ [treeController remove:sender];
+}
+
+
+- (void)_clearData
+{
+ [managedObjectContext reset];
+ [managedObjectContext release];
+ managedObjectContext = nil;
+}
+
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
+{
+ if ([menuItem action] == @selector(addHost:) ||
+ [menuItem action] == @selector(removeHost:))
+ {
+ return (([[treeController selectedObjects] count] > 0) &&
+ [[[treeController selectedObjects] objectAtIndex:0] parent] != nil);
+ }
+ return TRUE;
+}
+
+- (void)_selectParentFromSelection
+{
+ if ([[treeController selectedObjects] count] > 0)
+ {
+ NSTreeNode *firstSelectedNode = [[treeController selectedNodes] objectAtIndex:0];
+ NSTreeNode *parentNode = [firstSelectedNode parentNode];
+ if (parentNode)
+ {
+ NSIndexPath *parentIndex = [parentNode indexPath];
+ [treeController setSelectionIndexPath:parentIndex];
+ }
+ else
+ {
+ NSArray *selectedIndexPaths = [treeController selectionIndexPaths];
+ [treeController removeSelectionIndexPaths:selectedIndexPaths];
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark Notifications
+- (void)contextDidSave:(NSNotification *)notification
+{
+ if (!isInitializing)
+ {
+ NSArray *updated = [[notification userInfo] valueForKey:NSUpdatedObjectsKey];
+ NSArray *inserted = [[notification userInfo] valueForKey:NSInsertedObjectsKey];
+ NSArray *deleted = [[notification userInfo] valueForKey:NSDeletedObjectsKey];
+
+ if ([inserted count] > 0)
+ {
+ [self insertUsers:inserted];
+ }
+
+ if ([updated count] > 0)
+ {
+ [self updateUsers:updated];
+ }
+
+ if ([deleted count] > 0)
+ {
+ [self deleteUsers:deleted];
+ }
+ }
+}
+
+- (void)contextDidChange:(NSNotification *)notification
+{
+ NSLog(@"contextDidChange:");
+
+ if (!isInitializing)
+ {
+ [outlineView reloadData];
+ }
+}
+
+- (BOOL)updateUsers:(NSArray *)updatedUsers
+{
+ for (NSManagedObject *user in updatedUsers) {
+ [self grantPrivilegesToUser:user];
+ }
+ return TRUE;
+}
+
+- (BOOL)deleteUsers:(NSArray *)deletedUsers
+{
+ [[self connection] selectDB:@"mysql"];
+ NSMutableString *droppedUsers = [NSMutableString string];
+ for (NSManagedObject *user in deletedUsers)
+ {
+ if ([user host] != nil)
+ {
+ [droppedUsers appendString:[NSString stringWithFormat:@"%@@%@, ",
+ [[user valueForKey:@"user"] backtickQuotedString],
+ [[user valueForKey:@"host"] backtickQuotedString]]];
+ }
+
+ }
+ droppedUsers = [[droppedUsers substringToIndex:[droppedUsers length]-2] mutableCopy];
+ [[self connection] queryString:[NSString stringWithFormat:@"DROP USER %@", droppedUsers]];
+
+ return TRUE;
+}
+
+- (BOOL)insertUsers:(NSArray *)insertedUsers
+{
+ [[self connection] selectDB:@"mysql"];
+ for(NSManagedObject *user in insertedUsers)
+ {
+ NSString *createStatement = [NSString stringWithFormat:@"CREATE USER %@@%@ IDENTIFIED BY %@;",
+ [[[user parent] valueForKey:@"user"] tickQuotedString],
+ [[user valueForKey:@"host"] tickQuotedString],
+ [[[user parent] valueForKey:@"password"] tickQuotedString]];
+ // Create user in database
+ [[self connection] queryString:[NSString stringWithFormat:createStatement]];
+
+ if ([self checkAndDisplayMySqlError])
+ {
+ [self grantPrivilegesToUser:user];
+ }
+ }
+
+ return TRUE;
+
+}
+
+// Grant or Revoke privileges to the given user
+- (BOOL)grantPrivilegesToUser:(NSManagedObject *)user
+{
+ if ([user valueForKey:@"parent"] != nil)
+ {
+ NSDictionary *attributesDict = [[user entity] attributesByName];
+ NSMutableArray *grantPrivileges = [NSMutableArray array];
+ NSMutableArray *revokePrivileges = [NSMutableArray array];
+
+ for(NSString *key in [attributesDict allKeys])
+ {
+ if ([key hasSuffix:@"_priv"])
+ {
+ NSString *privilege = [key stringByReplacingOccurrencesOfString:@"_priv" withString:@""];
+
+ if ([[user valueForKey:key] boolValue] == TRUE)
+ {
+ [grantPrivileges addObject:[NSString stringWithFormat:@"%@", [privilege replaceUnderscoreWithSpace]]];
+ }
+ else
+ {
+ [revokePrivileges addObject:[NSString stringWithFormat:@"%@", [privilege replaceUnderscoreWithSpace]]];
+ }
+ }
+ }
+ // Grant privileges
+ if ([grantPrivileges count] > 0)
+ {
+ NSString *grantStatement = [NSString stringWithFormat:@"GRANT %@ ON *.* TO %@@%@;",
+ [grantPrivileges componentsJoinedByCommas],
+ [[[user parent] valueForKey:@"user"] tickQuotedString],
+ [[user valueForKey:@"host"] tickQuotedString]];
+ NSLog(@"%@", grantStatement);
+ [[self connection] queryString:[NSString stringWithFormat:grantStatement]];
+ [self checkAndDisplayMySqlError];
+ }
+
+ // Revoke privileges
+ if ([revokePrivileges count] > 0)
+ {
+ NSString *revokeStatement = [NSString stringWithFormat:@"REVOKE %@ ON *.* TO %@@%@;",
+ [revokePrivileges componentsJoinedByCommas],
+ [[[user parent] valueForKey:@"user"] tickQuotedString],
+ [[user valueForKey:@"host"] tickQuotedString]];
+ NSLog(@"%@", revokeStatement);
+ [[self connection] queryString:[NSString stringWithFormat:revokeStatement]];
+ [self checkAndDisplayMySqlError];
+ }
+ }
+ return TRUE;
+}
+- (NSArray *)_fetchUserWithUserName:(NSString *)username
+{
+ NSManagedObjectContext *moc = [self managedObjectContext];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@ AND parent == nil", username];
+ NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"SPUser"
+ inManagedObjectContext:moc];
+ NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
+
+ [request setEntity:entityDescription];
+ [request setPredicate:predicate];
+
+ NSError *error = nil;
+ NSArray *array = [moc executeFetchRequest:request error:&error];
+ if (error != nil)
+ {
+ [[NSApplication sharedApplication] presentError:error];
+ }
+
+ return array;
+}
+
+- (NSManagedObject *)_createNewSPUser
+{
+ NSManagedObject *user = [[NSEntityDescription insertNewObjectForEntityForName:@"SPUser"
+ inManagedObjectContext:[self managedObjectContext]] autorelease];
+
+ return user;
+}
+
+- (BOOL)checkAndDisplayMySqlError
+{
+ if (![[[self connection] getLastErrorMessage] isEqualToString:@""])
+ {
+ NSBeginAlertSheet(@"MySQL Error",
+ nil,
+ nil,
+ nil,
+ window,
+ self,
+ NULL,
+ NULL,
+ nil,
+ [[self connection] getLastErrorMessage]);
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+#pragma mark -
+#pragma mark Tab View Delegate methods
+- (BOOL)tabView:(NSTabView *)tabView shouldSelectTabViewItem:(NSTabViewItem *)tabViewItem
+{
+ if ([[treeController selectedObjects] count] == 0)
+ return FALSE;
+
+ id selectedObject = [[treeController selectedObjects] objectAtIndex:0];
+ if ([[tabViewItem identifier] isEqualToString:@"General"])
+ {
+ if ([selectedObject parent] == nil) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+ }
+ else if ([[tabViewItem identifier] isEqualToString:@"Global Privileges"] ||
+ [[tabViewItem identifier] isEqualToString:@"Resources"])
+ {
+ if ([selectedObject parent] != nil)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+@end
diff --git a/Source/SPUserManager.xcdatamodel/elements b/Source/SPUserManager.xcdatamodel/elements
new file mode 100644
index 00000000..6766bed3
--- /dev/null
+++ b/Source/SPUserManager.xcdatamodel/elements
Binary files differ
diff --git a/Source/SPUserManager.xcdatamodel/layout b/Source/SPUserManager.xcdatamodel/layout
new file mode 100644
index 00000000..07b18bfd
--- /dev/null
+++ b/Source/SPUserManager.xcdatamodel/layout
Binary files differ
diff --git a/Source/TableDocument.h b/Source/TableDocument.h
index 42be9e5a..480ef810 100644
--- a/Source/TableDocument.h
+++ b/Source/TableDocument.h
@@ -53,6 +53,8 @@ enum {
IBOutlet id databaseDataInstance;
IBOutlet id spHistoryControllerInstance;
IBOutlet id spExportControllerInstance;
+ IBOutlet id userManagerInstance;
+
IBOutlet NSSearchField *listFilterField;
@@ -126,8 +128,8 @@ enum {
- (NSString *)getHTMLforPrint;
+- (IBAction)showUserManager:(id)sender;
- (void)initQueryEditorWithString:(NSString *)query;
-
// Connection callback and methods
- (void) setConnection:(MCPConnection *)theConnection;
- (void)setShouldAutomaticallyConnect:(BOOL)shouldAutomaticallyConnect;
diff --git a/Source/TableDocument.m b/Source/TableDocument.m
index 02212647..ee314b65 100644
--- a/Source/TableDocument.m
+++ b/Source/TableDocument.m
@@ -47,6 +47,7 @@
#import "SPPreferenceController.h"
#import "SPPrintAccessory.h"
#import "QLPreviewPanel.h"
+#import "SPUserManager.h"
// Used for printing
#import "MGTemplateEngine.h"
@@ -2174,7 +2175,15 @@
[toolbarItem setTarget:self];
[toolbarItem setAction:@selector(viewRelations:)];
-
+ } else if ([itemIdentifier isEqualToString:@"SwitchToUserManagerToolbarItemIdentifier"]) {
+ [toolbarItem setLabel:NSLocalizedString(@"Users", @"toolbar item label for switching to the User Manager tab")];
+ [toolbarItem setPaletteLabel:NSLocalizedString(@"Users", @"toolbar item label for switching to the User Manager tab")];
+ //set up tooltip and image
+ [toolbarItem setToolTip:NSLocalizedString(@"Switch to the User Manager tab", @"tooltip for toolbar item for switching to the User Manager tab")];
+ [toolbarItem setImage:[NSImage imageNamed:NSImageNameEveryone]];
+ //set up the target action
+ [toolbarItem setTarget:self];
+ [toolbarItem setAction:@selector(showUserManager:)];
} else {
//itemIdentifier refered to a toolbar item that is not provided or supported by us or cocoa
toolbarItem = nil;
@@ -2199,6 +2208,7 @@
@"SwitchToRunQueryToolbarItemIdentifier",
@"SwitchToTableInfoToolbarItemIdentifier",
@"SwitchToTableRelationsToolbarItemIdentifier",
+ @"SwitchToUserManagerToolbarItemIdentifier",
NSToolbarCustomizeToolbarItemIdentifier,
NSToolbarFlexibleSpaceItemIdentifier,
NSToolbarSpaceItemIdentifier,
@@ -2220,6 +2230,7 @@
@"SwitchToRunQueryToolbarItemIdentifier",
NSToolbarFlexibleSpaceItemIdentifier,
@"HistoryNavigationToolbarItemIdentifier",
+ @"SwitchToUserManagerToolbarItemIdentifier",
@"ShowConsoleIdentifier",
nil];
}
@@ -2542,6 +2553,16 @@
if(queryEditorInitString) [queryEditorInitString release];
[super dealloc];
}
+
+- (void)showUserManager:(id)sender
+{
+ if (userManagerInstance == nil)
+ {
+ userManagerInstance = [[SPUserManager alloc] initWithConnection:mySQLConnection];
+ } else {
+ [userManagerInstance show];
+ }
+}
@end