LoginPanelController.m #1

  • //
  • guest/
  • alan_petersen/
  • piper/
  • mac/
  • main/
  • Perforce/
  • Classes/
  • WindowControllers/
  • LoginPanelController.m
  • View
  • Commits
  • Open Download .zip Download (13 KB)
//
//  LoginPanelController.m
//  Perforce
//
//  Created by Adam Czubernat on 13.05.2013.
//  Copyright (c) 2013 Perforce Software, Inc. All rights reserved.
//

#import "LoginPanelController.h"
#import <SystemConfiguration/SystemConfiguration.h>

#import "P4Workspace.h"
#import "P4Defaults.h"
#import "P4WorkspaceDefaults.h"
#import "ErrorPanelController.h"

@interface LoginPanelController () {
	
	NSString *host;
	NSString *username;
	NSArray *servers;

	enum {
		panelTypeLogin,
		panelTypeConnection,
		panelTypeRelogin,
	} panelType;
	
	// Login
	__weak IBOutlet NSView *loginView;
	__weak IBOutlet NSTextField *usernameTextField;
	__weak IBOutlet NSTextField *passwordTextField;
	__weak IBOutlet NSButton *loginCancelButton;
	
	// Connection
	__weak IBOutlet NSView *connectionView;
	__weak IBOutlet NSComboBox *connectionComboBox;
	__weak IBOutlet NSTextField *connectionAddressField;
	__weak IBOutlet NSTextField *connectionUsernameField;
	__weak IBOutlet NSTextField *connectionPasswordField;
	__weak IBOutlet NSButton *connectionCancelButton;
	
	// Relogin
	__weak IBOutlet NSView *reloginView;
	__weak IBOutlet NSTextField *reloginPasswordField;
	
	// Loading
	__weak IBOutlet NSView *loadingView;
	__weak IBOutlet NSProgressIndicator *loadingIndicator;
	__weak IBOutlet NSTextField *loadingLabel;
	
	// SSO
	__weak IBOutlet NSButton *ssoCheckbox;
}

- (void)loadWorkspace:(NSString *)workspace;
- (void)didLoadWorkspace:(NSArray *)workspaceInfo;
- (NSString *)defaultWorkspaceName;
- (void)loadDefaultWorkspace;
- (void)createDefaultWorkspace;

- (void)failWithError:(NSError *)error;
- (void)loginWithCredentials:(P4Credentials *)credentials;
- (void)didLogin;

- (void)showLoginView;
- (void)showConnectionView;
- (void)showReloginView;

- (IBAction)cancelPressed:(id)sender;

- (IBAction)loginPressed:(id)sender;
- (IBAction)connectionDetailsPressed:(id)sender;
- (IBAction)comboBoxSelected:(id)sender;
- (IBAction)connectPressed:(id)sender;

- (IBAction)reloginPressed:(id)sender;
- (IBAction)disconnectPressed:(id)sender;

- (IBAction)ssoCheckboxPressed:(id)sender;
@end

@implementation LoginPanelController
@synthesize delegate, allowsLastWorkspace, allowsSSOLogin;

- (void)windowDidLoad {
	[super windowDidLoad];
	
	host = [P4Defaults sharedInstance].host;
	username = [P4Defaults sharedInstance].username;
#ifdef DEBUG
//	[addressTextField setStringValue:@"test-server.homelinux.org:16660"];
//	[addressTextField setStringValue:@"ec2-50-16-43-78.compute-1.amazonaws.com:1666"];
//	[usernameTextField setStringValue:@"a.czubernat"];
//	[passwordTextField setStringValue:@"p@ssw0rd"];
	[passwordTextField setStringValue:@"adam23"];
#endif
	
	if (panelType == panelTypeLogin) {
		if (host.length)
			[self showLoginView];
		else
			[self showConnectionView];
	} else if (panelType == panelTypeConnection) {
		[self showConnectionView];
	} else if (panelType == panelTypeRelogin) {
		[self showReloginView];
	}
}

#pragma mark - Public

+ (LoginPanelController *)connectionPanel {
	LoginPanelController *panel = [[LoginPanelController alloc] init];
	panel->panelType = panelTypeConnection;
	return panel;
}

+ (LoginPanelController *)loginPanel {
	LoginPanelController *panel = [[LoginPanelController alloc] init];
	panel->panelType = panelTypeLogin;
	return panel;
	
}

+ (LoginPanelController *)reloginPanel {
	LoginPanelController *panel = [[LoginPanelController alloc] init];
	panel->panelType = panelTypeRelogin;
	return panel;
}

#pragma mark - Private

- (void)loadWorkspace:(NSString *)workspace {
	[[P4Workspace sharedInstance]
	 setWorkspace:workspace
	 response:^(P4Operation *operation, NSArray *response) {
		 [loadingIndicator stopAnimation:nil];
		 if (operation.errors) {
			 PSLog(@"Couldn't use workspace %@ %@", workspace, operation.error);
			 [self didLogin];
		 } else {
			 PSLog(@"Connected to workspace: %@", workspace);
			 [self didLoadWorkspace:response];
		 }
	 }];
}

- (void)didLoadWorkspace:(NSArray *)workspaceInfo {
	PSLogStore(@"Workspace info", @"%@", workspaceInfo);
	NSString *workspace = [[workspaceInfo firstObject] objectForKey:@"Client"];

	// Save last workspace details
	[P4WorkspaceDefaults setWorkspace:workspace];
	
	if ([delegate respondsToSelector:@selector(loginPanelDidLoginWithWorkspace:)])
		[delegate loginPanelDidLoginWithWorkspace:workspace];
}

- (NSString *)defaultWorkspaceName {
	NSString *machine = (__bridge NSString *)SCDynamicStoreCopyLocalHostName(NULL);
	return [NSString stringWithFormat:@"piper.%@.%@", machine, username];
}

- (void)loadDefaultWorkspace {
	NSString *workspace = [self defaultWorkspaceName];
	
	[[P4Workspace sharedInstance]
	 setWorkspace:workspace
	 response:^(P4Operation *operation, NSArray *response) {
		 NSArray *unknownClient = [operation errorsWithCode:P4ErrorUnknownClient];
		 [operation ignoreErrors:unknownClient];
		 if (operation.errors) {
			 [self failWithError:operation.error];
		 } else if (unknownClient) {
			 if ([[NSAlert
				   alertWithMessageText:@"\nWorkspace not found"
				   defaultButton:@"Create"
				   alternateButton:@"No"
				   otherButton:nil
				   informativeTextWithFormat:@"Do you want to create a default workspace?"]
				  runModal] != NSAlertDefaultReturn) {
				 [self didLogin]; // Skip workspace loading
				 return;
			 }
			 // Create workspace
			 PSLog(@"Creating default workspace...");
			 [self createDefaultWorkspace];
		 } else {
			 PSLog(@"Connected to default workspace: %@", workspace);
			 [self didLoadWorkspace:response];
		 }
	 }];
}

- (void)createDefaultWorkspace {
	[loadingLabel setStringValue:@"Configuring workspace..."];
	
	NSString *workspace = [self defaultWorkspaceName];
	
	NSURL *publicURL = [[[NSFileManager defaultManager]
						 URLsForDirectory:NSSharedPublicDirectory
						 inDomains:NSUserDomainMask] firstObject];
	NSURL *rootURL = [publicURL URLByAppendingPathComponent:workspace isDirectory:YES];
	if (![rootURL checkResourceIsReachableAndReturnError:NULL]) {
		[[NSFileManager defaultManager]
		 createDirectoryAtURL:rootURL withIntermediateDirectories:NO
		 attributes:nil error:NULL];
	}
	
	NSDictionary *workspaceSpecs =
	@{ @"Client" :	workspace,
	   @"Owner" :	[[P4Workspace sharedInstance] username],
	   @"Description" : @"Created by Piper",
	   @"Root" :	rootURL.path,
	   @"Options" :	@"noallwrite clobber rmdir nocompress unlocked",
	   @"SubmitOptions" : @"revertunchanged",
	   @"LineEnd" : @"local",
	   };

	[[P4Workspace sharedInstance]
	 createWorkspace:workspaceSpecs
	 response:^(P4Operation *operation, NSArray *response) {
		 if (operation.errors) {
			 [self failWithError:operation.error];
			 return;
		 }
		 PSLog(@"Created default workspace %@", workspace);
		 [self loadWorkspace:workspace];
	 }];
}

- (void)failWithError:(NSError *)error {
	[loadingIndicator stopAnimation:nil];
	[self showLoginView];
	
	ErrorPanelController *alert = [[ErrorPanelController alloc] init];
	[alert setTitle:@"\nLogin failed"];
    if ([error.localizedDescription rangeOfString:@"Connect to server failed"].location != NSNotFound) {
        [alert setMessage:@"It seems like we lost connection to the server. Please ensure that you are on the network."];
    } else {
        [alert setMessage:error.localizedDescription];
    }
	[alert presentWithMainWindow];
}

- (void)loginWithCredentials:(P4Credentials *)credentials {

	[self setPresentedView:loadingView animated:YES];
	[loadingIndicator startAnimation:nil];
	[loadingLabel setStringValue:@"Logging in..."];
	
	// Logout if logged in
	if ([[P4Workspace sharedInstance] isLoggedIn]) {
		[[P4Workspace sharedInstance] logout:^(P4Operation *operation, NSArray *response) {
			[self loginWithCredentials:credentials];
		}];
		return;
	}
	
	PSLog(@"Logging in...");
	
	// Response block
	P4ResponseBlock_t loginResponseBlock = ^(P4Operation *operation, NSArray *response) {		
		if (operation.errors) {
			[self failWithError:operation.error];
			return;
		}
		// Connected
		PSLog(@"Connected");
		// Save last login details
		[P4Defaults sharedInstance].host = credentials.address;
		[P4Defaults sharedInstance].username = credentials.username;
		
		// Automatic workspace selection
		NSString *lastWorkspace = [[P4Defaults sharedInstance].users objectForKey:username];
		if (allowsLastWorkspace && lastWorkspace) {
			PSLog(@"Loading last workspace \"%@\"...", lastWorkspace);
			[self loadWorkspace:lastWorkspace];
		// Load default workspace
		} else {
			PSLog(@"Loading default workspace...");
			[self loadDefaultWorkspace];
		}
	};
	
	// Connect with specified host
	[[P4Workspace sharedInstance]
	 connectWithCredentials:credentials
	 response:^(P4Operation *operation, NSArray *response) {		
		 if (operation.errors) {
			 [self failWithError:operation.error];
			 return;
		 }

		 if (allowsSSOLogin) {
			 [[P4Workspace sharedInstance]
			  loginWithSSO:credentials
			  response:loginResponseBlock];
		 } else {
			 [[P4Workspace sharedInstance]
			  loginWithCredentials:credentials
			  response:loginResponseBlock];
		 }
	 }];
}

- (void)didLogin {
	[loadingIndicator stopAnimation:nil];
	[self showLoginView];
	if ([delegate respondsToSelector:@selector(loginPanelDidLogin)])
		[delegate loginPanelDidLogin];
}

- (void)showLoginView {
	
	[loadingIndicator stopAnimation:nil];
	usernameTextField.stringValue = username ?: @"";
	[loginCancelButton setEnabled:
	 [[P4Workspace sharedInstance] isLoggedIn] &&
	 [[P4Workspace sharedInstance] workspace]];
		
	[self setPresentedView:loginView animated:YES];
}

- (void)showConnectionView {

	connectionAddressField.stringValue = host ?: @"";
	connectionUsernameField.stringValue = username ?: @"";
	[connectionCancelButton setEnabled:
	 [[P4Workspace sharedInstance] isLoggedIn] &&
	 [[P4Workspace sharedInstance] workspace]];
	
	// Load servers list
	NSString *plist = [[NSBundle mainBundle] pathForResource:@"Servers" ofType:@"plist"];
	servers = [NSArray arrayWithContentsOfFile:plist];
	NSArray *names = [servers valueForKeyPath:@"name"];
	NSArray *addresses = [servers valueForKeyPath:@"address"];
	
	[connectionComboBox removeAllItems];
	[connectionComboBox addItemsWithObjectValues:names];
	
	// Select server if last address matches one in the list
	NSInteger idx = [addresses indexOfObject:host];
	if (idx != NSNotFound)
		[connectionComboBox selectItemAtIndex:idx];
	
	[ssoCheckbox setState:allowsSSOLogin];
	
	[self setPresentedView:connectionView animated:YES];
	[self.window makeFirstResponder:connectionPasswordField];
}

- (void)showReloginView {
	[self setPresentedView:reloginView animated:YES];
}

#pragma mark - Actions

- (IBAction)cancelPressed:(id)sender {
	if ([delegate respondsToSelector:@selector(loginPanelDidCancel)])
		[delegate loginPanelDidCancel];
}

- (IBAction)loginPressed:(NSButton *)sender {
	
	P4Credentials *credentials = [[P4Credentials alloc] init];
	credentials.address = host;
	credentials.username = username = usernameTextField.stringValue;
	credentials.password = passwordTextField.stringValue;
	connectionPasswordField.stringValue = passwordTextField.stringValue;
	
	[self loginWithCredentials:credentials];
}

- (IBAction)connectionDetailsPressed:(id)sender {

	username = usernameTextField.stringValue;
	connectionPasswordField.stringValue = passwordTextField.stringValue;
	
	[self showConnectionView];
}

- (IBAction)comboBoxSelected:(id)sender {
	NSInteger idx = [connectionComboBox indexOfSelectedItem];
	NSString *address = [[servers objectAtIndex:idx] objectForKey:@"address"];
	connectionAddressField.stringValue = address ?: @"";
}

- (IBAction)connectPressed:(id)sender {

	P4Credentials *credentials = [[P4Credentials alloc] init];
	credentials.address = host = connectionAddressField.stringValue;
	credentials.username = username = connectionUsernameField.stringValue;
	credentials.password = connectionPasswordField.stringValue;
	passwordTextField.stringValue = connectionPasswordField.stringValue;
	
	[self loginWithCredentials:credentials];
}

- (IBAction)reloginPressed:(NSButton *)sender {
	
	P4Credentials *credentials = [[P4Credentials alloc] init];
	credentials.password = reloginPasswordField.stringValue;

	sender.enabled = NO;
	
	[self setPresentedView:loadingView animated:YES];
	[loadingIndicator startAnimation:nil];
	[loadingLabel setStringValue:@"Reconnecting..."];

	[[P4Workspace sharedInstance]
	 reloginWithCredentials:credentials
	 response:^(P4Operation *operation, NSArray *response) {
		
		 sender.enabled = YES;
		 [loadingIndicator stopAnimation:nil];

		 if (operation.errors) {
			 [self setPresentedView:reloginView animated:YES];
			 
			 PSLog(@"Failed to reconnect");
			 NSError *error = operation.error;
			 ErrorPanelController *alert = [[ErrorPanelController alloc] init];
			 [alert setTitle:@"\nFailed to reconnect"];
			 [alert setMessage:error.localizedDescription];
			 [alert presentWithMainWindow];
			 
		 } else {
			 NSString *lastWorkspace = [P4Defaults sharedInstance].workspace;
			 if (![P4Workspace sharedInstance].workspace && lastWorkspace) {
				 [self loadWorkspace:lastWorkspace];
			 } else {
				 if ([delegate respondsToSelector:@selector(loginPanelDidRelogin)])
					 [delegate loginPanelDidRelogin];
			 }
		 }
	 }];
}

- (IBAction)disconnectPressed:(id)sender {
	
	[self setPresentedView:loadingView animated:YES];
	[loadingIndicator startAnimation:nil];
	[loadingLabel setStringValue:@"Logging out..."];

	[[P4Workspace sharedInstance] logout:^(P4Operation *operation, NSArray *response) {
		[loadingIndicator stopAnimation:nil];
		[self setPresentedView:loginView animated:YES];
	}];

}

- (IBAction)ssoCheckboxPressed:(id)sender {
	allowsSSOLogin = ssoCheckbox.state == NSOnState;
}

@end
# Change User Description Committed
#1 15071 alan_petersen Populate -o //guest/perforce_software/piper/...
//guest/alan_petersen/piper/....
//guest/perforce_software/piper/mac/main/Perforce/Classes/WindowControllers/LoginPanelController.m
#3 13625 alan_petersen UPDATE:
- updated p4api to p4api-2015.1.1054991
- included in OpenSSL 1.0.2a ssl and crypto libraries

The benefit of these are that Piper now supports connecting to 15.1 servers, and also supports ssl: connections. Woo hoo!
#2 12961 alan_petersen Piper 2.0 Mega Update

New Features/Functionality
- Added help menu redirecting to URL.
- Added readonly property for creating new workspaces.
- Added html hyperlinks for Copy link functionality.
- Added functionality for managing Finder Favorite items in sidebar.
- Redesigned the way mapping is stored in Piper.
- First version of syncing finder sidebar items with workspace mapping.
- Small sorting improvements.
- Creating Projects directory inside users home folder.
- Adding Projects folder to finder sidebar item.
- Creating and removing symbolic links accordingly to mapped folders.
- Preventing duplicate names in symbolic links.
- Refreshing symbolic links on mapping change inside application.
- Storing workspace and server details in p4 configuration for other applications to use.
- Added contextual menu items for Finder integration.
- Added services menu for Adobe Illustrator integration.
- Keyboard shortcuts for Illustrator integration.
- Code refactoring and fixes for mapping issues.
- Added Finder functionality to edit all files in folder.
- Added user friendly message when editing a file using Finder outside the workspace.
- Implemented hidden automatic login when opening application using Finder integration.
- Logging to file in ~/Library/Logs
- Unified workspace and all files views to show both local and depot files and folders.
- Removed my workspace view references and logic.
- Editing unmapped files on server.
- First version of adding file to unmapped folders.
- Showing opened by and edit actions in column details for all depot files.
- Improved mappings functionality.
- Enabled same feature options for mapped and unmapped folders and files.
- Redesigned from scratch mapping and unmapping procedures for adding and removing files.
- Implemented cleaning workspace using new mapping functionality. Removed debug overlay coloring.
- Automated workspace creation
- Improvements in editing files already mapped to workspace.
- Implemented deleting remote files.
- Implemented first version of move operation for remote files.
- Removing last workspace information when disconnecting from workspace using app menu.
- Implemented editing and submitting using symbolic links in project folder. New finder menu service for symbolic links Show in Piper which acts like share link functionality.
- New icons for files and folders not tracked in the filesystem.
- Improvements in showing file using share link.
- Switched to new way of retrieving files in order to show user changes.
- Redesigned and implemented new functionality for chaining operations with mapping.
- Improvements and redesign of Edit/add actions to use new chaining logic . Fixed issue with file edit.
- Improvements in window showing when using services.
- Simplified file loading so the local files appears only when remote are also loaded.
- Improved deleting of untracked files to avoid mapping and marking for delete.
- Enabling simple copy paste and moving of remote and local files.
- Added abort for exception handling in order to force crashing application on critical failures
- Added custom exception handling for catching runtime errors to log and crash instead of continuing in unstable state.
- Changed file copying to use mark for add .
- Simplified and fixed responding file representations to mapping changes.

Bug Fixes
- Fixed crash when synchronizing.
- Fixed sync issue when downloading directory without file size information.
- Fixed issue with unread list crashing when file is not existing on disk.
- Fixed incorrect sync progress calculation.
- Removed relative path issues.
- Fixed many of case-sensitivity problems.
- Fixed deprecated methods and related issues in OS X 10.10.
- Fixed folder rename not updating in column view. Revised and fixed many potential problems from implicit casting.
- Fixed missing sync button on fast sync completion.
- Refreshing mapping on synchronization. Fixed symbolic links not appearing until app is restarted.
- Fixed latest crashing of autosync.
- Fixed loading indicator issues.
- Fixed and redesigned submit dialog to work correctly with Submit All Files option in Finder.
- Fixed multiple error messages on network outage. Redesigned showing errors in main window.
- Fixed opening random locations when using Finder integration.
- Fixed issue when panel was detached from parent window.
- Fixed bug when creating new workspace wouldn't store default settings.
- Fixed memory issues with network operations.
- Fixes in relogging mappings and file listing.
- Improvements in editing unmapped files.
- Fixed crash when adding file outside workspace.
- Fixed breadcrumbs control issue.
- Fixed issue with double parent folders when opening unmapped files.
- Fixed crashes on sync after mapping new files.
- Fixed issue with editing file using Finder
-- Merging code and additional fixes in add button functionality.
- Fixed unsync not working
- Fixed submit panel issue not selecting files with different name case.
- Fixed missing revert and sync to workspace actions in some cases.
- Fixed issue with Submit and Edit finder actions. Improvements in stability of finder integration.
- Fixed issue with unsubmitted folders breaking status of files inside.
- Fixed issue with added files not showing correct icon and status.
- Fixed bug with file edit resulting in a new directory named exactly like a file.
- Fixed issue with reloading of subpath resulting in untracked folders.
- Fixed mapping issue when result was always view mapping not relative.
- Fixed submit panel showing more than once.
- Fixed illustrator services not working.
- Fixed userdefaults preferences problem with workspace name being null.
- Fixed userdefaults keypath problem of dot-containing workspace names.
- Forcing recreating of browser to possibly prevent pre-10.10 errors with automatic workspace selection.
- Fixed adding file to depot not presenting correct icon.
- Fixed issues with reverting a file that was marked for add.
- Presenting error when trying to submit untracked files.
- Fixed issue when submit files service crashed when using unmapped files.
- Fixed file representation disappearing when removing file.
- Fixed issue with symlinks resolving working on 10.10 only. Issue related to workspace selection not showing.
- Fixed error panel method calls unavailable in Mac OS versions before 10.10. Issue related to hanging error panels.
- Fixed removing a local file resulting in action progress freezing.
- Fixed open file not working after edit.
- Fixing crash when mapping changed. Issue related to moving local file to unmapped folder and other similar cases.
#1 11252 alan_petersen Rename/move file(s)
//guest/perforce_software/piper/mac/Perforce/Classes/WindowControllers/LoginPanelController.m
#1 10744 alan_petersen Rename/move file(s)
//guest/perforce_software/piper/Perforce/Classes/WindowControllers/LoginPanelController.m
#1 8919 Matt Attaway Initial add of Piper, a lightweight Perforce client for artists and designers.