mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Refresh master with initial release/0.24 snapshot (#332)
* Initial port of release/0.24 source code * Fix additional headers * Fix a typo in launch.json
This commit is contained in:
@@ -8,15 +8,14 @@
|
||||
import 'vs/css!./media/accountDialog';
|
||||
import 'vs/css!sql/parts/accountManagement/common/media/accountActions';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { SplitView } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { SplitView } from 'sql/base/browser/ui/splitview/splitview';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachListStyler, attachButtonStyler } from 'vs/platform/theme/common/styler';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { ActionRunner } from 'vs/base/common/actions';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -25,8 +24,9 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||
|
||||
import * as data from 'data';
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import { Modal } from 'sql/base/browser/ui/modal/modal';
|
||||
import { attachModalDialogStyler } from 'sql/common/theme/styler';
|
||||
import { attachModalDialogStyler, attachButtonStyler } from 'sql/common/theme/styler';
|
||||
import { AccountViewModel } from 'sql/parts/accountManagement/accountDialog/accountViewModel';
|
||||
import { AddAccountAction } from 'sql/parts/accountManagement/common/accountActions';
|
||||
import { AccountListRenderer, AccountListDelegate } from 'sql/parts/accountManagement/common/accountListRenderer';
|
||||
@@ -67,7 +67,7 @@ export class AccountDialog extends Modal {
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
) {
|
||||
super(
|
||||
localize('linkedAccounts', 'Linked Accounts'),
|
||||
localize('linkedAccounts', 'Linked accounts'),
|
||||
TelemetryKeys.Accounts,
|
||||
partService,
|
||||
telemetryService,
|
||||
@@ -81,7 +81,7 @@ export class AccountDialog extends Modal {
|
||||
this._actionRunner = new ActionRunner();
|
||||
|
||||
// Setup the event emitters
|
||||
this._onAddAccountErrorEmitter = new Emitter<string>();
|
||||
this._onAddAccountErrorEmitter = new Emitter<string>();
|
||||
this._onCloseEmitter = new Emitter<void>();
|
||||
|
||||
// Create the view model and wire up the events
|
||||
|
||||
@@ -18,8 +18,6 @@ import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
|
||||
|
||||
export class AccountListStatusbarItem implements IStatusbarItem {
|
||||
private _rootElement: HTMLElement;
|
||||
private _iconElement: HTMLElement;
|
||||
private _toDispose: IDisposable[];
|
||||
private _manageLinkedAccountAction: IAction;
|
||||
|
||||
@@ -32,11 +30,11 @@ export class AccountListStatusbarItem implements IStatusbarItem {
|
||||
|
||||
public render(container: HTMLElement): IDisposable {
|
||||
// Create root element for account list
|
||||
this._rootElement = append(container, $('.linked-account-staus'));
|
||||
this._rootElement.title = ManageLinkedAccountAction.LABEL;
|
||||
this._rootElement.onclick = () => this._onClick();
|
||||
|
||||
this._iconElement = append(this._rootElement, $('a.linked-account-status-selection'));
|
||||
const rootElement = append(container, $('.linked-account-staus'));
|
||||
const accountElement = append(rootElement, $('a.linked-account-status-selection'));
|
||||
accountElement.title = ManageLinkedAccountAction.LABEL;
|
||||
accountElement.onclick = () => this._onClick();
|
||||
append(accountElement, $('.linked-account-icon'));
|
||||
|
||||
return combinedDisposable(this._toDispose);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,14 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.statusbar-item .linked-account-staus a.linked-account-status-selection {
|
||||
background: url('accounts_statusbar_inverse.svg') center center no-repeat;
|
||||
background-size: 12px;
|
||||
height: 12px !important;
|
||||
.statusbar-item .linked-account-staus a.linked-account-status-selection .linked-account-icon {
|
||||
-webkit-mask: url('accounts_statusbar_inverse.svg') no-repeat 50% 50%;
|
||||
-webkit-mask-size: 12px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
width: 12px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.statusbar-item .linked-account-staus a.linked-account-status-selection {
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ export class AccountPicker extends Disposable {
|
||||
private _refreshContainer: HTMLElement;
|
||||
private _listContainer: HTMLElement;
|
||||
private _dropdown: DropdownList;
|
||||
private _refreshAccountAction: RefreshAccountAction;
|
||||
|
||||
// EVENTING ////////////////////////////////////////////////////////////
|
||||
private _addAccountCompleteEmitter: Emitter<void>;
|
||||
@@ -62,13 +63,6 @@ export class AccountPicker extends Disposable {
|
||||
this._addAccountStartEmitter = new Emitter<void>();
|
||||
this._onAccountSelectionChangeEvent = new Emitter<data.Account>();
|
||||
|
||||
// Create an account list
|
||||
let delegate = new AccountListDelegate(AccountPicker.ACCOUNTPICKERLIST_HEIGHT);
|
||||
let accountRenderer = new AccountPickerListRenderer();
|
||||
this._listContainer = DOM.$('div.account-list-container');
|
||||
this._accountList = new List<data.Account>(this._listContainer, delegate, [accountRenderer]);
|
||||
this._register(attachListStyler(this._accountList, this._themeService));
|
||||
|
||||
// Create the view model, wire up the events, and initialize with baseline data
|
||||
this.viewModel = this._instantiationService.createInstance(AccountPickerViewModel, this._providerId);
|
||||
this.viewModel.updateAccountListEvent(arg => {
|
||||
@@ -76,8 +70,6 @@ export class AccountPicker extends Disposable {
|
||||
this.updateAccountList(arg.accountList);
|
||||
}
|
||||
});
|
||||
|
||||
this.createAccountPickerComponent();
|
||||
}
|
||||
|
||||
// PUBLIC METHODS //////////////////////////////////////////////////////
|
||||
@@ -88,7 +80,18 @@ export class AccountPicker extends Disposable {
|
||||
DOM.append(container, this._rootElement);
|
||||
}
|
||||
|
||||
private createAccountPickerComponent() {
|
||||
// PUBLIC METHODS //////////////////////////////////////////////////////
|
||||
/**
|
||||
* Create account picker component
|
||||
*/
|
||||
public createAccountPickerComponent() {
|
||||
// Create an account list
|
||||
let delegate = new AccountListDelegate(AccountPicker.ACCOUNTPICKERLIST_HEIGHT);
|
||||
let accountRenderer = new AccountPickerListRenderer();
|
||||
this._listContainer = DOM.$('div.account-list-container');
|
||||
this._accountList = new List<data.Account>(this._listContainer, delegate, [accountRenderer]);
|
||||
this._register(attachListStyler(this._accountList, this._themeService));
|
||||
|
||||
this._rootElement = DOM.$('div.account-picker-container');
|
||||
|
||||
// Create a dropdown for account picker
|
||||
@@ -116,7 +119,8 @@ export class AccountPicker extends Disposable {
|
||||
this._refreshContainer = DOM.append(this._rootElement, DOM.$('div.refresh-container'));
|
||||
DOM.append(this._refreshContainer, DOM.$('div.icon warning'));
|
||||
let actionBar = new ActionBar(this._refreshContainer, { animated: false });
|
||||
actionBar.push(new RefreshAccountAction(RefreshAccountAction.ID, RefreshAccountAction.LABEL), { icon: false, label: true });
|
||||
this._refreshAccountAction = this._instantiationService.createInstance(RefreshAccountAction);
|
||||
actionBar.push(this._refreshAccountAction, { icon: false, label: true });
|
||||
|
||||
if (this._accountList.length > 0) {
|
||||
this._accountList.setSelection([0]);
|
||||
@@ -146,10 +150,12 @@ export class AccountPicker extends Disposable {
|
||||
private onAccountSelectionChange(account: data.Account) {
|
||||
this.viewModel.selectedAccount = account;
|
||||
if (account && account.isStale) {
|
||||
this._refreshAccountAction.account = account;
|
||||
new Builder(this._refreshContainer).show();
|
||||
} else {
|
||||
new Builder(this._refreshContainer).hide();
|
||||
}
|
||||
|
||||
this._onAccountSelectionChangeEvent.fire(account);
|
||||
}
|
||||
|
||||
@@ -170,9 +176,9 @@ export class AccountPicker extends Disposable {
|
||||
const badgeContent = DOM.append(badge, DOM.$('div.badge-content'));
|
||||
const label = DOM.append(row, DOM.$('div.label'));
|
||||
|
||||
icon.className = 'icon';
|
||||
// Set the account icon
|
||||
icon.style.background = `url('data:${account.displayInfo.contextualLogo.light}')`;
|
||||
icon.classList.add('icon', account.displayInfo.accountType);
|
||||
|
||||
// TODO: Pick between the light and dark logo
|
||||
label.innerText = account.displayInfo.displayName + ' (' + account.displayInfo.contextualDisplayName + ')';
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ export class AccountPickerService implements IAccountPickerService {
|
||||
// TODO: expand support to multiple providers
|
||||
const providerId: string = 'azurePublicCloud';
|
||||
this._accountPicker = this._instantiationService.createInstance(AccountPicker, providerId);
|
||||
this._accountPicker.createAccountPickerComponent();
|
||||
}
|
||||
|
||||
this._accountPicker.addAccountCompleteEvent(() => this._addAccountCompleteEmitter.fire());
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
import 'vs/css!./media/autoOAuthDialog';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import { Modal } from 'sql/base/browser/ui/modal/modal';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { attachModalDialogStyler, attachButtonStyler } from 'sql/common/theme/styler';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||
|
||||
export class AutoOAuthDialog extends Modal {
|
||||
private _copyAndOpenButton: Button;
|
||||
private _closeButton: Button;
|
||||
private _userCodeInputBox: InputBox;
|
||||
private _websiteInputBox: InputBox;
|
||||
private _descriptionElement: HTMLElement;
|
||||
|
||||
// EVENTING ////////////////////////////////////////////////////////////
|
||||
private _onHandleAddAccount = new Emitter<void>();
|
||||
public get onHandleAddAccount(): Event<void> { return this._onHandleAddAccount.event; }
|
||||
|
||||
private _onCancel = new Emitter<void>();
|
||||
public get onCancel(): Event<void> { return this._onCancel.event; }
|
||||
|
||||
|
||||
private _onCloseEvent = new Emitter<void>();
|
||||
public get onCloseEvent(): Event<void> { return this._onCloseEvent.event; }
|
||||
|
||||
constructor(
|
||||
@IPartService partService: IPartService,
|
||||
@IThemeService private _themeService: IThemeService,
|
||||
@IContextViewService private _contextViewService: IContextViewService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
) {
|
||||
super(
|
||||
'',
|
||||
TelemetryKeys.AutoOAuth,
|
||||
partService,
|
||||
telemetryService,
|
||||
contextKeyService,
|
||||
{
|
||||
isFlyout: true,
|
||||
hasBackButton: true,
|
||||
hasSpinner: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public render() {
|
||||
super.render();
|
||||
attachModalDialogStyler(this, this._themeService);
|
||||
this.backButton.addListener('click', () => this.cancel());
|
||||
this._register(attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND }));
|
||||
|
||||
this._copyAndOpenButton = this.addFooterButton(localize('copyAndOpen', 'Copy & Open'), () => this.addAccount());
|
||||
this._closeButton = this.addFooterButton(localize('cancel', 'Cancel'), () => this.cancel());
|
||||
this.registerListeners();
|
||||
this._userCodeInputBox.disable();
|
||||
this._websiteInputBox.disable();
|
||||
}
|
||||
|
||||
protected layout(height?: number): void {
|
||||
// NO OP
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement) {
|
||||
$().div({ class: 'auto-oauth-description-section new-section' }, (descriptionContainer) => {
|
||||
this._descriptionElement = descriptionContainer.getHTMLElement();
|
||||
});
|
||||
|
||||
let addAccountSection;
|
||||
$().div({ class: 'auto-oauth-info-section new-section' }, (addAccountContainer) => {
|
||||
addAccountSection = addAccountContainer.getHTMLElement();
|
||||
this._userCodeInputBox = this.createInputBoxHelper(addAccountContainer, localize('userCode', 'User code'));
|
||||
this._websiteInputBox = this.createInputBoxHelper(addAccountContainer, localize('website', 'Website'));
|
||||
});
|
||||
|
||||
new Builder(container).div({ class: 'auto-oauth-dialog' }, (builder) => {
|
||||
builder.append(this._descriptionElement);
|
||||
builder.append(addAccountSection);
|
||||
});
|
||||
}
|
||||
|
||||
private createInputBoxHelper(container: Builder, label: string): InputBox {
|
||||
let inputBox: InputBox;
|
||||
container.div({ class: 'dialog-input-section' }, (inputContainer) => {
|
||||
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
|
||||
labelContainer.innerHtml(label);
|
||||
});
|
||||
|
||||
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
|
||||
inputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService);
|
||||
});
|
||||
});
|
||||
return inputBox;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
// Theme styler
|
||||
this._register(attachButtonStyler(this._copyAndOpenButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._closeButton, this._themeService));
|
||||
this._register(attachInputBoxStyler(this._userCodeInputBox, this._themeService));
|
||||
this._register(attachInputBoxStyler(this._websiteInputBox, this._themeService));
|
||||
|
||||
}
|
||||
|
||||
/* Overwrite escape key behavior */
|
||||
protected onClose() {
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
/* Overwrite enter key behavior */
|
||||
protected onAccept() {
|
||||
this.addAccount();
|
||||
}
|
||||
|
||||
private addAccount() {
|
||||
if (this._copyAndOpenButton.enabled) {
|
||||
this._copyAndOpenButton.enabled = false;
|
||||
this.showSpinner();
|
||||
this._onHandleAddAccount.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
this._onCancel.fire();
|
||||
}
|
||||
|
||||
public close() {
|
||||
this._copyAndOpenButton.enabled = true;
|
||||
this._onCloseEvent.fire();
|
||||
this.hideSpinner();
|
||||
this.hide();
|
||||
}
|
||||
|
||||
public open(title: string, message: string, userCode: string, uri: string) {
|
||||
// Update dialog
|
||||
this.title = title;
|
||||
this._descriptionElement.innerText = message;
|
||||
this._userCodeInputBox.value = userCode;
|
||||
this._websiteInputBox.value = uri;
|
||||
this.show();
|
||||
this._copyAndOpenButton.focus();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { AutoOAuthDialog } from 'sql/parts/accountManagement/autoOAuthDialog/autoOAuthDialog';
|
||||
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
|
||||
|
||||
export class AutoOAuthDialogController {
|
||||
// MEMBER VARIABLES ////////////////////////////////////////////////////
|
||||
private _autoOAuthDialog: AutoOAuthDialog;
|
||||
private _providerId: string;
|
||||
private _userCode: string;
|
||||
private _uri: string;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IAccountManagementService private _accountManagementService: IAccountManagementService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService
|
||||
) {
|
||||
this._providerId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open auto OAuth dialog
|
||||
*/
|
||||
public openAutoOAuthDialog(providerId: string, title: string, message: string, userCode: string, uri: string): Thenable<void> {
|
||||
if (this._providerId !== null) {
|
||||
// If a oauth flyout is already open, return an error
|
||||
let errorMessage = localize('oauthFlyoutIsAlreadyOpen', 'Cannot start auto OAuth. An auto OAuth is already in progress.');
|
||||
this._errorMessageService.showDialog(Severity.Error, '', errorMessage);
|
||||
return Promise.reject(new Error('Auto OAuth dialog already open'));
|
||||
}
|
||||
|
||||
// Create a new dialog if one doesn't exist
|
||||
if (!this._autoOAuthDialog) {
|
||||
this._autoOAuthDialog = this._instantiationService.createInstance(AutoOAuthDialog);
|
||||
this._autoOAuthDialog.onHandleAddAccount(this.handleOnAddAccount, this);
|
||||
this._autoOAuthDialog.onCancel(this.handleOnCancel, this);
|
||||
this._autoOAuthDialog.onCloseEvent(this.handleOnClose, this);
|
||||
this._autoOAuthDialog.render();
|
||||
}
|
||||
|
||||
this._userCode = userCode;
|
||||
this._uri = uri;
|
||||
|
||||
// Open the dialog
|
||||
this._autoOAuthDialog.open(title, message, userCode, uri);
|
||||
this._providerId = providerId;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close auto OAuth dialog
|
||||
*/
|
||||
public closeAutoOAuthDialog(): void {
|
||||
this._autoOAuthDialog.close();
|
||||
this._providerId = null;
|
||||
}
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
private handleOnCancel(): void {
|
||||
this._accountManagementService.cancelAutoOAuthDeviceCode(this._providerId);
|
||||
}
|
||||
|
||||
private handleOnClose(): void {
|
||||
this._providerId = null;
|
||||
}
|
||||
|
||||
private handleOnAddAccount(): void {
|
||||
this._accountManagementService.copyUserCodeAndOpenBrowser(this._userCode, this._uri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.auto-oauth-dialog {
|
||||
padding: 15px
|
||||
}
|
||||
|
||||
.modal .auto-oauth-dialog .new-section {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.modal .auto-oauth-dialog .dialog-input-section {
|
||||
display: flex;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.modal .auto-oauth-dialog .dialog-input-section .dialog-label {
|
||||
flex: 0 0 100px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.modal .auto-oauth-dialog .dialog-input-section .dialog-input {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
@@ -36,8 +36,6 @@ export class AddAccountAction extends Action {
|
||||
|
||||
constructor(
|
||||
private _providerId: string,
|
||||
@IMessageService private _messageService: IMessageService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService,
|
||||
@IAccountManagementService private _accountManagementService: IAccountManagementService
|
||||
) {
|
||||
super(AddAccountAction.ID, AddAccountAction.LABEL);
|
||||
@@ -99,21 +97,25 @@ export class RemoveAccountAction extends Action {
|
||||
type: 'question'
|
||||
};
|
||||
|
||||
if (!this._messageService.confirm(confirm)) {
|
||||
return TPromise.as(false);
|
||||
}
|
||||
let confirmPromise = this._messageService.confirm(confirm);
|
||||
|
||||
return new TPromise((resolve, reject) => {
|
||||
self._accountManagementService.removeAccount(self._account.key)
|
||||
.then(
|
||||
(result) => { resolve(result); },
|
||||
(err) => {
|
||||
// Must handle here as this is an independent action
|
||||
self._errorMessageService.showDialog(Severity.Error,
|
||||
localize('removeAccountFailed', 'Failed to remove account'), err);
|
||||
resolve(false);
|
||||
}
|
||||
);
|
||||
return confirmPromise.then(confirmation => {
|
||||
if (!confirmation.confirmed) {
|
||||
return TPromise.as(false);
|
||||
} else {
|
||||
return new TPromise((resolve, reject) => {
|
||||
self._accountManagementService.removeAccount(self._account.key)
|
||||
.then(
|
||||
(result) => { resolve(result); },
|
||||
(err) => {
|
||||
// Must handle here as this is an independent action
|
||||
self._errorMessageService.showDialog(Severity.Error,
|
||||
localize('removeAccountFailed', 'Failed to remove account'), err);
|
||||
resolve(false);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -144,15 +146,31 @@ export class ApplyFilterAction extends Action {
|
||||
export class RefreshAccountAction extends Action {
|
||||
public static ID = 'account.refresh';
|
||||
public static LABEL = localize('refreshAccount', 'Reenter your credentials');
|
||||
public account: data.Account;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string
|
||||
@IAccountManagementService private _accountManagementService: IAccountManagementService
|
||||
) {
|
||||
super(id, label, 'refresh-account-action icon refresh');
|
||||
super(RefreshAccountAction.ID, RefreshAccountAction.LABEL, 'refresh-account-action icon refresh');
|
||||
}
|
||||
public run(): TPromise<boolean> {
|
||||
// Todo: refresh the account
|
||||
return TPromise.as(true);
|
||||
let self = this;
|
||||
return new TPromise((resolve, reject) => {
|
||||
if (self.account) {
|
||||
self._accountManagementService.refreshAccount(self.account)
|
||||
.then(
|
||||
() => {
|
||||
resolve(true);
|
||||
},
|
||||
err => {
|
||||
error(`Error while refreshing account: ${err}`);
|
||||
reject(err);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
let errorMessage = localize('NoAccountToRefresh', 'There is no account to refresh');
|
||||
reject(errorMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { ActionBar, IActionOptions } from 'vs/base/browser/ui/actionbar/actionba
|
||||
import { localize } from 'vs/nls';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
import { RemoveAccountAction, ApplyFilterAction, RefreshAccountAction } from 'sql/parts/accountManagement/common/accountActions';
|
||||
import { RemoveAccountAction, RefreshAccountAction } from 'sql/parts/accountManagement/common/accountActions';
|
||||
|
||||
import * as data from 'data';
|
||||
|
||||
@@ -66,9 +66,7 @@ export class AccountPickerListRenderer implements IRenderer<data.Account, Accoun
|
||||
|
||||
public renderElement(account: data.Account, index: number, templateData: AccountListTemplate): void {
|
||||
// Set the account icon
|
||||
templateData.icon.classList.add('account-logo');
|
||||
templateData.icon.style.background = `url('data:${account.displayInfo.contextualLogo.light}')`;
|
||||
// TODO: Pick between the light and dark logo
|
||||
templateData.icon.classList.add('account-logo', account.displayInfo.accountType);
|
||||
|
||||
templateData.contextualDisplayName.innerText = account.displayInfo.contextualDisplayName;
|
||||
templateData.displayName.innerText = account.displayInfo.displayName;
|
||||
@@ -114,9 +112,12 @@ export class AccountListRenderer extends AccountPickerListRenderer {
|
||||
|
||||
let actionOptions: IActionOptions = { icon: true, label: false };
|
||||
if (account.isStale) {
|
||||
templateData.actions.push(new RefreshAccountAction(RefreshAccountAction.ID, RefreshAccountAction.LABEL), actionOptions);
|
||||
let refreshAction = this._instantiationService.createInstance(RefreshAccountAction);
|
||||
refreshAction.account = account;
|
||||
templateData.actions.push(refreshAction, actionOptions);
|
||||
} else {
|
||||
templateData.actions.push(new ApplyFilterAction(ApplyFilterAction.ID, ApplyFilterAction.LABEL), actionOptions);
|
||||
// Todo: Will show filter action when API/GUI for filtering is implemented (#3022, #3024)
|
||||
// templateData.actions.push(new ApplyFilterAction(ApplyFilterAction.ID, ApplyFilterAction.LABEL), actionOptions);
|
||||
}
|
||||
|
||||
let removeAction = this._instantiationService.createInstance(RemoveAccountAction, account);
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { IExtensionPointUser, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { localize } from 'vs/nls';
|
||||
import { join } from 'path';
|
||||
import { createCSSRule } from 'vs/base/browser/dom';
|
||||
import URI from 'vs/base/common/uri';
|
||||
|
||||
import { ManageLinkedAccountAction } from 'sql/parts/accountManagement/accountListStatusbar/accountListStatusbarItem';
|
||||
|
||||
let actionRegistry = <IWorkbenchActionRegistry>Registry.as(Extensions.WorkbenchActions);
|
||||
|
||||
actionRegistry.registerWorkbenchAction(
|
||||
new SyncActionDescriptor(
|
||||
ManageLinkedAccountAction,
|
||||
ManageLinkedAccountAction.ID,
|
||||
ManageLinkedAccountAction.LABEL
|
||||
),
|
||||
ManageLinkedAccountAction.LABEL
|
||||
);
|
||||
|
||||
export interface IAccountContrib {
|
||||
id: string;
|
||||
icon?: IUserFriendlyIcon;
|
||||
}
|
||||
|
||||
export type IUserFriendlyIcon = string | { light: string; dark: string; };
|
||||
|
||||
const account: IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
description: localize('carbon.extension.contributes.account.id', 'Identifier of the account type'),
|
||||
type: 'string'
|
||||
},
|
||||
icon: {
|
||||
description: localize('carbon.extension.contributes.account.icon', '(Optional) Icon which is used to represent the accpunt in the UI. Either a file path or a themable configuration'),
|
||||
anyOf: [{
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
light: {
|
||||
description: localize('carbon.extension.contributes.account.icon.light', 'Icon path when a light theme is used'),
|
||||
type: 'string'
|
||||
},
|
||||
dark: {
|
||||
description: localize('carbon.extension.contributes.account.icon.dark', 'Icon path when a dark theme is used'),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const accountsContribution: IJSONSchema = {
|
||||
description: localize('carbon.extension.contributes.account', "Contributes icons to account provider."),
|
||||
oneOf: [
|
||||
account,
|
||||
{
|
||||
type: 'array',
|
||||
items: account
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
ExtensionsRegistry.registerExtensionPoint<IAccountContrib | IAccountContrib[]>('account-type', [], accountsContribution).setHandler(extensions => {
|
||||
|
||||
function handleCommand(account: IAccountContrib, extension: IExtensionPointUser<any>) {
|
||||
let { icon, id } = account;
|
||||
let iconClass: string;
|
||||
if (icon) {
|
||||
iconClass = id;
|
||||
if (typeof icon === 'string') {
|
||||
const path = join(extension.description.extensionFolderPath, icon);
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: url("${URI.file(path).toString()}")`);
|
||||
} else {
|
||||
const light = join(extension.description.extensionFolderPath, icon.light);
|
||||
const dark = join(extension.description.extensionFolderPath, icon.dark);
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: url("${URI.file(light).toString()}")`);
|
||||
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${URI.file(dark).toString()}")`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let extension of extensions) {
|
||||
const { value } = extension;
|
||||
if (Array.isArray<IAccountContrib>(value)) {
|
||||
for (let command of value) {
|
||||
handleCommand(command, extension);
|
||||
}
|
||||
} else {
|
||||
handleCommand(value, extension);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
'use strict';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { IResourceProviderService, IHandleFirewallRuleResult } from 'sql/parts/accountManagement/common/interfaces';
|
||||
@@ -26,7 +25,6 @@ export class ResourceProviderService implements IResourceProviderService {
|
||||
constructor(
|
||||
@ITelemetryService private _telemetryService: ITelemetryService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IEnvironmentService private _environmentService: IEnvironmentService
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -66,39 +64,33 @@ export class ResourceProviderService implements IResourceProviderService {
|
||||
* Handle a firewall rule
|
||||
*/
|
||||
public handleFirewallRule(errorCode: number, errorMessage: string, connectionTypeId: string): Promise<IHandleFirewallRuleResult> {
|
||||
if (!this._environmentService.isBuilt) {
|
||||
let self = this;
|
||||
return new Promise<IHandleFirewallRuleResult>((resolve, reject) => {
|
||||
let handleFirewallRuleResult: IHandleFirewallRuleResult;
|
||||
let promises = [];
|
||||
if (self._providers) {
|
||||
for (let key in self._providers) {
|
||||
let provider = self._providers[key];
|
||||
promises.push(provider.handleFirewallRule(errorCode, errorMessage, connectionTypeId)
|
||||
.then(response => {
|
||||
if (response.result) {
|
||||
handleFirewallRuleResult = { canHandleFirewallRule: response.result, ipAddress: response.ipAddress, resourceProviderId: key };
|
||||
}
|
||||
},
|
||||
() => { /* Swallow failures at getting accounts, we'll just hide that provider */
|
||||
}));
|
||||
}
|
||||
let self = this;
|
||||
return new Promise<IHandleFirewallRuleResult>((resolve, reject) => {
|
||||
let handleFirewallRuleResult: IHandleFirewallRuleResult;
|
||||
let promises = [];
|
||||
if (self._providers) {
|
||||
for (let key in self._providers) {
|
||||
let provider = self._providers[key];
|
||||
promises.push(provider.handleFirewallRule(errorCode, errorMessage, connectionTypeId)
|
||||
.then(response => {
|
||||
if (response.result) {
|
||||
handleFirewallRuleResult = { canHandleFirewallRule: response.result, ipAddress: response.ipAddress, resourceProviderId: key };
|
||||
}
|
||||
},
|
||||
() => { /* Swallow failures at getting accounts, we'll just hide that provider */
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
if (handleFirewallRuleResult) {
|
||||
resolve(handleFirewallRuleResult);
|
||||
} else {
|
||||
handleFirewallRuleResult = { canHandleFirewallRule: false, ipAddress: undefined, resourceProviderId: undefined };
|
||||
resolve(handleFirewallRuleResult);
|
||||
}
|
||||
});
|
||||
Promise.all(promises).then(() => {
|
||||
if (handleFirewallRuleResult) {
|
||||
resolve(handleFirewallRuleResult);
|
||||
} else {
|
||||
handleFirewallRuleResult = { canHandleFirewallRule: false, ipAddress: undefined, resourceProviderId: undefined };
|
||||
resolve(handleFirewallRuleResult);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return new Promise<IHandleFirewallRuleResult>((resolve, reject) => {
|
||||
resolve({ canHandleFirewallRule: false, ipAddress: undefined, resourceProviderId: undefined });
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,24 +8,23 @@
|
||||
import 'vs/css!./media/firewallRuleDialog';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { localize } from 'vs/nls';
|
||||
import { buttonBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { attachButtonStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
import * as data from 'data';
|
||||
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import { Modal } from 'sql/base/browser/ui/modal/modal';
|
||||
import { FirewallRuleViewModel } from 'sql/parts/accountManagement/firewallRuleDialog/firewallRuleViewModel';
|
||||
import { attachModalDialogStyler } from 'sql/common/theme/styler';
|
||||
import { attachModalDialogStyler, attachButtonStyler } from 'sql/common/theme/styler';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { IAccountPickerService } from 'sql/parts/accountManagement/common/interfaces';
|
||||
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||
|
||||
@@ -10,7 +10,7 @@ import { localize } from 'vs/nls';
|
||||
import * as data from 'data';
|
||||
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { IConnectionManagementService, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { FirewallRuleDialog } from 'sql/parts/accountManagement/firewallRuleDialog/firewallRuleDialog';
|
||||
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
|
||||
import { IResourceProviderService } from 'sql/parts/accountManagement/common/interfaces';
|
||||
@@ -29,7 +29,6 @@ export class FirewallRuleDialogController {
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IResourceProviderService private _resourceProviderService: IResourceProviderService,
|
||||
@IConnectionManagementService private _connectionService: IConnectionManagementService,
|
||||
@IAccountManagementService private _accountManagementService: IAccountManagementService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user