Use remote file dialog for database and server properties dialogs (#24390)

This commit is contained in:
Cory Rivera
2023-09-13 10:49:10 -07:00
committed by GitHub
parent 4f155423dc
commit 08ded51e75
31 changed files with 108 additions and 97 deletions

View File

@@ -209,7 +209,7 @@
"update-grammar": "node ../../build/npm/update-grammar.js Microsoft/vscode-azuremonitor ./syntaxes/azuremonitor.tmLanguage" "update-grammar": "node ../../build/npm/update-grammar.js Microsoft/vscode-azuremonitor ./syntaxes/azuremonitor.tmLanguage"
}, },
"dependencies": { "dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.7", "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.8",
"figures": "^2.0.0", "figures": "^2.0.0",
"find-remove": "1.2.1", "find-remove": "1.2.1",
"@microsoft/ads-service-downloader": "^1.2.1", "@microsoft/ads-service-downloader": "^1.2.1",

View File

@@ -75,9 +75,9 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.3.7": "dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.3.8":
version "1.3.7" version "1.3.8"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/0f07d03394eeebc2924971746470ac8224348fa4" resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/828b7a5e5c1c077a0f6eb7f6acecd191fbaae13d"
dependencies: dependencies:
vscode-languageclient "5.2.1" vscode-languageclient "5.2.1"

View File

@@ -107,7 +107,7 @@
"dependencies": { "dependencies": {
"@microsoft/ads-extension-telemetry": "^3.0.1", "@microsoft/ads-extension-telemetry": "^3.0.1",
"@microsoft/ads-service-downloader": "^1.2.1", "@microsoft/ads-service-downloader": "^1.2.1",
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.7", "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.8",
"vscode-nls": "^5.2.0" "vscode-nls": "^5.2.0"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -497,9 +497,9 @@ crypt@0.0.2:
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.3.7": "dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.3.8":
version "1.3.7" version "1.3.8"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/0f07d03394eeebc2924971746470ac8224348fa4" resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/828b7a5e5c1c077a0f6eb7f6acecd191fbaae13d"
dependencies: dependencies:
vscode-languageclient "5.2.1" vscode-languageclient "5.2.1"

View File

@@ -77,7 +77,7 @@
} }
}, },
"dependencies": { "dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.7", "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.8",
"htmlparser2": "^3.10.1", "htmlparser2": "^3.10.1",
"@microsoft/ads-service-downloader": "^1.2.1", "@microsoft/ads-service-downloader": "^1.2.1",
"@microsoft/ads-extension-telemetry": "^3.0.1", "@microsoft/ads-extension-telemetry": "^3.0.1",

View File

@@ -572,9 +572,9 @@ crypt@~0.0.1:
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.3.7": "dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.3.8":
version "1.3.7" version "1.3.8"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/0f07d03394eeebc2924971746470ac8224348fa4" resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/828b7a5e5c1c077a0f6eb7f6acecd191fbaae13d"
dependencies: dependencies:
vscode-languageclient "5.2.1" vscode-languageclient "5.2.1"

View File

@@ -427,7 +427,7 @@
} }
}, },
"dependencies": { "dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.7", "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.8",
"figures": "^2.0.0", "figures": "^2.0.0",
"find-remove": "1.2.1", "find-remove": "1.2.1",
"@microsoft/ads-service-downloader": "^1.2.1", "@microsoft/ads-service-downloader": "^1.2.1",

View File

@@ -124,9 +124,9 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.3.7": "dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.3.8":
version "1.3.7" version "1.3.8"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/0f07d03394eeebc2924971746470ac8224348fa4" resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/828b7a5e5c1c077a0f6eb7f6acecd191fbaae13d"
dependencies: dependencies:
vscode-languageclient "5.2.1" vscode-languageclient "5.2.1"

View File

@@ -1,6 +1,6 @@
{ {
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/{#version#}/microsoft.sqltools.servicelayer-{#fileName#}", "downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "4.10.0.1", "version": "4.10.0.2",
"downloadFileNames": { "downloadFileNames": {
"Windows_86": "win-x86-net7.0.zip", "Windows_86": "win-x86-net7.0.zip",
"Windows_64": "win-x64-net7.0.zip", "Windows_64": "win-x64-net7.0.zip",

View File

@@ -1572,7 +1572,7 @@
"dependencies": { "dependencies": {
"@microsoft/ads-extension-telemetry": "^3.0.1", "@microsoft/ads-extension-telemetry": "^3.0.1",
"@microsoft/ads-service-downloader": "^1.2.1", "@microsoft/ads-service-downloader": "^1.2.1",
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.7", "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.8",
"find-remove": "1.2.1", "find-remove": "1.2.1",
"vscode-languageclient": "5.2.1", "vscode-languageclient": "5.2.1",
"vscode-nls": "^4.0.0" "vscode-nls": "^4.0.0"

View File

@@ -62,16 +62,20 @@ export class AttachDatabaseDialog extends ObjectManagementDialogBase<Database, D
const buttonContainer = this.addButtonsForTable(this._databasesTable, addButton, removeButton); const buttonContainer = this.addButtonsForTable(this._databasesTable, addButton, removeButton);
this._nameField = this.createInputBox(async newValue => { this._nameField = this.createInputBox(async newValue => {
let selectedRow = this._databasesTable.selectedRows[0]; if (this._databasesTable.selectedRows?.length > 0) {
let dbFile = this._databasesToAttach[selectedRow]; let selectedRow = this._databasesTable.selectedRows[0];
dbFile.databaseName = newValue; let dbFile = this._databasesToAttach[selectedRow];
dbFile.databaseName = newValue;
}
}, {}); }, {});
this._nameContainer = this.createLabelInputContainer(loc.AttachAsText, this._nameField); this._nameContainer = this.createLabelInputContainer(loc.AttachAsText, this._nameField);
this._ownerDropdown = this.createDropdown(loc.OwnerText, async newValue => { this._ownerDropdown = this.createDropdown(loc.OwnerText, async newValue => {
let selectedRow = this._databasesTable.selectedRows[0]; if (this._databasesTable.selectedRows?.length > 0) {
let dbFile = this._databasesToAttach[selectedRow]; let selectedRow = this._databasesTable.selectedRows[0];
dbFile.owner = newValue; let dbFile = this._databasesToAttach[selectedRow];
dbFile.owner = newValue;
}
}, this.viewInfo.loginNames.options, this.viewInfo.loginNames.options[this.viewInfo.loginNames.defaultValueIndex]); }, this.viewInfo.loginNames.options, this.viewInfo.loginNames.options[this.viewInfo.loginNames.defaultValueIndex]);
this._ownerContainer = this.createLabelInputContainer(loc.OwnerText, this._ownerDropdown); this._ownerContainer = this.createLabelInputContainer(loc.OwnerText, this._ownerDropdown);

View File

@@ -738,8 +738,9 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
defaultFileGrowthInMb: defaultFileGrowthInMb, defaultFileGrowthInMb: defaultFileGrowthInMb,
defaultFileGrowthInPercent: defaultFileGrowthInPercent, defaultFileGrowthInPercent: defaultFileGrowthInPercent,
defaultMaxFileSizeLimitedToInMb: defaultMaxFileSizeLimitedToInMb defaultMaxFileSizeLimitedToInMb: defaultMaxFileSizeLimitedToInMb
} },
}); connectionUri: this.options.connectionUri
}, this.objectManagementService);
await dialog.open(); await dialog.open();
return await dialog.waitForClose(); return await dialog.waitForClose();
} }

View File

@@ -4,13 +4,13 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata'; import * as azdata from 'azdata';
import * as vscode from 'vscode';
import * as path from 'path'; import * as path from 'path';
import { DefaultInputWidth, DialogBase } from '../../ui/dialogBase'; import { DefaultInputWidth, DialogBase } from '../../ui/dialogBase';
import * as localizedConstants from '../localizedConstants'; import * as localizedConstants from '../localizedConstants';
import { DatabaseFile, DatabaseViewInfo, FileGrowthType } from '../interfaces'; import { DatabaseFile, DatabaseViewInfo, FileGrowthType } from '../interfaces';
import { isUndefinedOrNull } from '../../types'; import { isUndefinedOrNull } from '../../types';
import { deepClone } from '../../util/objects'; import { deepClone } from '../../util/objects';
import { IObjectManagementService } from 'mssql';
export interface NewDatabaseFileDialogOptions { export interface NewDatabaseFileDialogOptions {
title: string; title: string;
@@ -27,6 +27,7 @@ export interface NewDatabaseFileDialogOptions {
defaultFileGrowthInMb: number, defaultFileGrowthInMb: number,
defaultMaxFileSizeLimitedToInMb: number defaultMaxFileSizeLimitedToInMb: number
}; };
connectionUri: string;
} }
const fileSizeInputMaxValueInMbForDataType = 16776192; // Row type supports up to 16 TB (SSMS allows =~ 15.99TB) const fileSizeInputMaxValueInMbForDataType = 16776192; // Row type supports up to 16 TB (SSMS allows =~ 15.99TB)
@@ -58,7 +59,7 @@ export class DatabaseFileDialog extends DialogBase<DatabaseFile> {
private originalFileName: string; private originalFileName: string;
private isEditingFile: boolean; private isEditingFile: boolean;
constructor(private readonly options: NewDatabaseFileDialogOptions) { constructor(private readonly options: NewDatabaseFileDialogOptions, private readonly objectManagementService: IObjectManagementService) {
super(options.title, 'DatabaseFileDialog'); super(options.title, 'DatabaseFileDialog');
} }
@@ -296,23 +297,12 @@ export class DatabaseFileDialog extends DialogBase<DatabaseFile> {
* Creates a file browser and sets the path to the filePath * Creates a file browser and sets the path to the filePath
*/ */
private async createFileBrowser(): Promise<void> { private async createFileBrowser(): Promise<void> {
let fileUris = await vscode.window.showOpenDialog( let dataFolder = await this.objectManagementService.getDataFolder(this.options.connectionUri);
{ let filePath = await azdata.window.openServerFileBrowserDialog(this.options.connectionUri, dataFolder, [{ label: localizedConstants.allFiles, filters: ['*'] }], true);
canSelectFiles: false, if (filePath?.length > 0) {
canSelectFolders: true, this.filePathTextBox.value = filePath;
canSelectMany: false, this.result.path = filePath;
defaultUri: vscode.Uri.file(this.options.databaseFile.path),
openLabel: localizedConstants.SelectText
}
);
if (!fileUris || fileUris.length === 0) {
return;
} }
let fileUri = fileUris[0];
this.filePathTextBox.value = fileUri.fsPath;
this.result.path = fileUri.fsPath;
} }
/** /**
@@ -332,7 +322,7 @@ export class DatabaseFileDialog extends DialogBase<DatabaseFile> {
if (selectedOption === localizedConstants.LogFiletype) { if (selectedOption === localizedConstants.LogFiletype) {
fileGroupDdOptions = [localizedConstants.FileGroupForLogTypeText]; fileGroupDdOptions = [localizedConstants.FileGroupForLogTypeText];
fileGroupDdValue = localizedConstants.FileGroupForLogTypeText; fileGroupDdValue = localizedConstants.FileGroupForLogTypeText;
fileSizeInputMaxValue = fileSizeInputMaxValueInMbForLogType fileSizeInputMaxValue = fileSizeInputMaxValueInMbForLogType;
} }
// File Stream // File Stream
else if (selectedOption === localizedConstants.FilestreamFileType) { else if (selectedOption === localizedConstants.FilestreamFileType) {

View File

@@ -661,21 +661,8 @@ export class ServerPropertiesDialog extends ObjectManagementDialogBase<Server, S
} }
public async selectFolder(location: string): Promise<string | undefined> { public async selectFolder(location: string): Promise<string | undefined> {
const allFilesFilter = localizedConstants.allFiles; let dataFolder = await this.objectManagementService.getDataFolder(this.options.connectionUri);
let filter: any = {}; return await azdata.window.openServerFileBrowserDialog(this.options.connectionUri, dataFolder, [{ label: localizedConstants.allFiles, filters: ['*'] }], true);
filter[allFilesFilter] = '*';
let uris = await vscode.window.showOpenDialog({
filters: filter,
canSelectFiles: false,
canSelectMany: false,
canSelectFolders: true,
defaultUri: vscode.Uri.file(location),
openLabel: localizedConstants.labelSelectFolder
});
if (uris && uris.length > 0) {
return uris[0].fsPath;
}
return undefined;
} }
private initializeAdvancedSection(): void { private initializeAdvancedSection(): void {

View File

@@ -511,9 +511,9 @@ crypt@~0.0.1:
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.3.7": "dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.3.8":
version "1.3.7" version "1.3.8"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/0f07d03394eeebc2924971746470ac8224348fa4" resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/828b7a5e5c1c077a0f6eb7f6acecd191fbaae13d"
dependencies: dependencies:
vscode-languageclient "5.2.1" vscode-languageclient "5.2.1"

View File

@@ -162,7 +162,7 @@
] ]
}, },
"dependencies": { "dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.7", "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.8",
"@microsoft/ads-service-downloader": "^1.2.1", "@microsoft/ads-service-downloader": "^1.2.1",
"@microsoft/ads-extension-telemetry": "^3.0.1", "@microsoft/ads-extension-telemetry": "^3.0.1",
"uuid": "^8.3.2", "uuid": "^8.3.2",

View File

@@ -62,9 +62,9 @@ chownr@^2.0.0:
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.3.7": "dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#1.3.8":
version "1.3.7" version "1.3.8"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/0f07d03394eeebc2924971746470ac8224348fa4" resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/828b7a5e5c1c077a0f6eb7f6acecd191fbaae13d"
dependencies: dependencies:
vscode-languageclient "5.2.1" vscode-languageclient "5.2.1"

View File

@@ -2068,9 +2068,14 @@ declare module 'azdata' {
* @param connectionUri The URI of the connection to the target server * @param connectionUri The URI of the connection to the target server
* @param targetPath The file path on the server machine to open by default in the dialog * @param targetPath The file path on the server machine to open by default in the dialog
* @param fileFilters The filters used to limit which files are displayed in the file browser * @param fileFilters The filters used to limit which files are displayed in the file browser
* @param showFoldersOnly Optional argument to specify whether the browser should only show folders
* @returns The path of the file chosen from the dialog, and undefined if the dialog is closed without selecting anything. * @returns The path of the file chosen from the dialog, and undefined if the dialog is closed without selecting anything.
*/ */
export function openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: FileFilters[]): Thenable<string | undefined>; export function openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: FileFilters[], showFoldersOnly?: boolean): Thenable<string | undefined>;
}
export interface FileBrowserProvider extends DataProvider {
openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean, showFoldersOnly?: boolean): Thenable<boolean>;
} }
export interface TableComponent { export interface TableComponent {

View File

@@ -338,8 +338,8 @@ export class MainThreadDataProtocol extends Disposable implements MainThreadData
public $registerFileBrowserProvider(providerId: string, handle: number): Promise<any> { public $registerFileBrowserProvider(providerId: string, handle: number): Promise<any> {
const self = this; const self = this;
this._fileBrowserService.registerProvider(providerId, <azdata.FileBrowserProvider>{ this._fileBrowserService.registerProvider(providerId, <azdata.FileBrowserProvider>{
openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable<boolean> { openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean, showFoldersOnly?: boolean): Thenable<boolean> {
return self._proxy.$openFileBrowser(handle, ownerUri, expandPath, fileFilters, changeFilter); return self._proxy.$openFileBrowser(handle, ownerUri, expandPath, fileFilters, changeFilter, showFoldersOnly);
}, },
expandFolderNode(ownerUri: string, expandPath: string): Thenable<boolean> { expandFolderNode(ownerUri: string, expandPath: string): Thenable<boolean> {
return self._proxy.$expandFolderNode(handle, ownerUri, expandPath); return self._proxy.$expandFolderNode(handle, ownerUri, expandPath);

View File

@@ -20,13 +20,13 @@ export class MainThreadWindow extends Disposable implements MainThreadWindowShap
super(); super();
} }
public async $openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined> { public async $openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[], showFoldersOnly?: boolean): Promise<string | undefined> {
let completion = new Promise<string | undefined>((resolve, reject) => { let completion = new Promise<string | undefined>((resolve, reject) => {
try { try {
const handleOnClosed = (path: string | undefined) => { const handleOnClosed = (path: string | undefined) => {
resolve(path); resolve(path);
}; };
this._fileBrowserDialogService.showDialog(connectionUri, targetPath, fileFilters, '', true, handleOnClosed); this._fileBrowserDialogService.showDialog(connectionUri, targetPath, fileFilters, '', true, handleOnClosed, showFoldersOnly);
} catch (error) { } catch (error) {
reject(error); reject(error);
} }

View File

@@ -632,8 +632,8 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
/** /**
* Open a file browser * Open a file browser
*/ */
public override $openFileBrowser(handle: number, ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable<boolean> { public override $openFileBrowser(handle: number, ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean, showFoldersOnly?: boolean): Thenable<boolean> {
return this._resolveProvider<azdata.FileBrowserProvider>(handle).openFileBrowser(ownerUri, expandPath, fileFilters, changeFilter); return this._resolveProvider<azdata.FileBrowserProvider>(handle).openFileBrowser(ownerUri, expandPath, fileFilters, changeFilter, showFoldersOnly);
} }
/** /**

View File

@@ -17,7 +17,7 @@ export class ExtHostWindow implements ExtHostWindowShape {
this._proxy = _mainContext.getProxy(SqlMainContext.MainThreadWindow); this._proxy = _mainContext.getProxy(SqlMainContext.MainThreadWindow);
} }
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined> { $openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[], showFoldersOnly?: boolean): Promise<string | undefined> {
return this._proxy.$openServerFileBrowserDialog(connectionUri, targetPath, fileFilters); return this._proxy.$openServerFileBrowserDialog(connectionUri, targetPath, fileFilters, showFoldersOnly);
} }
} }

View File

@@ -488,8 +488,8 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
openCustomErrorDialog(options: sqlExtHostTypes.IErrorDialogOptions): Thenable<string | undefined> { openCustomErrorDialog(options: sqlExtHostTypes.IErrorDialogOptions): Thenable<string | undefined> {
return extHostModelViewDialog.openCustomErrorDialog(options); return extHostModelViewDialog.openCustomErrorDialog(options);
}, },
openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Thenable<string | undefined> { openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[], showFoldersOnly?: boolean): Thenable<string | undefined> {
return extHostWindow.$openServerFileBrowserDialog(connectionUri, targetPath, fileFilters); return extHostWindow.$openServerFileBrowserDialog(connectionUri, targetPath, fileFilters, showFoldersOnly);
} }
}; };

View File

@@ -355,7 +355,7 @@ export abstract class ExtHostDataProtocolShape {
/** /**
* Open a file browser * Open a file browser
*/ */
$openFileBrowser(handle: number, ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable<boolean> { throw ni(); } $openFileBrowser(handle: number, ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean, showFoldersOnly?: boolean): Thenable<boolean> { throw ni(); }
/** /**
@@ -833,7 +833,7 @@ export interface ExtHostWorkspaceShape {
} }
export interface ExtHostWindowShape { export interface ExtHostWindowShape {
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined>; $openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[], showFoldersOnly?: boolean): Promise<string | undefined>;
} }
export interface MainThreadWorkspaceShape { export interface MainThreadWorkspaceShape {
@@ -843,7 +843,7 @@ export interface MainThreadWorkspaceShape {
} }
export interface MainThreadWindowShape { export interface MainThreadWindowShape {
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined>; $openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[], showFoldersOnly?: boolean): Promise<string | undefined>;
} }
export interface MainThreadBackgroundTaskManagementShape extends IDisposable { export interface MainThreadBackgroundTaskManagementShape extends IDisposable {

View File

@@ -38,6 +38,8 @@ export class FileBrowserDialog extends Modal {
private _body: HTMLElement; private _body: HTMLElement;
private _filePathInputBox: InputBox; private _filePathInputBox: InputBox;
private _fileFilterSelectBox: SelectBox; private _fileFilterSelectBox: SelectBox;
private _fileFilterRow: HTMLElement;
private _originalFilterDisplay: string;
private _okButton: Button; private _okButton: Button;
private _onOk = new Emitter<string>(); private _onOk = new Emitter<string>();
public onOk: Event<string> = this._onOk.event; public onOk: Event<string> = this._onOk.event;
@@ -99,6 +101,8 @@ export class FileBrowserDialog extends Modal {
this._fileFilterSelectBox.setAriaLabel(filterLabel); this._fileFilterSelectBox.setAriaLabel(filterLabel);
let filterBuilder = DialogHelper.appendRow(tableContainer, filterLabel, 'file-input-label', 'file-input-box'); let filterBuilder = DialogHelper.appendRow(tableContainer, filterLabel, 'file-input-label', 'file-input-box');
DialogHelper.appendInputSelectBox(filterBuilder, this._fileFilterSelectBox); DialogHelper.appendInputSelectBox(filterBuilder, this._fileFilterSelectBox);
this._fileFilterRow = tableContainer.childNodes[1] as HTMLElement;
this._originalFilterDisplay = this._fileFilterRow.style.display;
this._okButton = this.addFooterButton(localize('fileBrowser.ok', "OK"), () => this.ok()); this._okButton = this.addFooterButton(localize('fileBrowser.ok', "OK"), () => this.ok());
this._okButton.enabled = false; this._okButton.enabled = false;
@@ -112,10 +116,17 @@ export class FileBrowserDialog extends Modal {
expandPath: string, expandPath: string,
fileFilters: [{ label: string, filters: string[] }], fileFilters: [{ label: string, filters: string[] }],
fileValidationServiceType: string, fileValidationServiceType: string,
showFoldersOnly?: boolean
): void { ): void {
this._viewModel.initialize(ownerUri, expandPath, fileFilters, fileValidationServiceType); this._viewModel.initialize(ownerUri, expandPath, fileFilters, fileValidationServiceType, showFoldersOnly);
this._viewModel.openFileBrowser(0, false).catch(err => onUnexpectedError(err)); this._viewModel.openFileBrowser(0, false).catch(err => onUnexpectedError(err));
this._fileFilterSelectBox.setOptions(this._viewModel.formattedFileFilters, 0); if (showFoldersOnly) {
this._fileFilterSelectBox.setOptions([]);
this._fileFilterRow.style.display = 'none';
} else {
this._fileFilterSelectBox.setOptions(this._viewModel.formattedFileFilters, 0);
this._fileFilterRow.style.display = this._originalFilterDisplay;
}
this._filePathInputBox.value = expandPath; this._filePathInputBox.value = expandPath;
this._isFolderSelected = true; this._isFolderSelected = true;
this.enableOkButton(); this.enableOkButton();
@@ -129,7 +140,7 @@ export class FileBrowserDialog extends Modal {
/* enter key */ /* enter key */
protected override onAccept() { protected override onAccept() {
if (this._okButton.enabled === true) { if (this._okButton.enabled) {
this.ok(); this.ok();
} }
} }
@@ -140,7 +151,7 @@ export class FileBrowserDialog extends Modal {
} }
private enableOkButton() { private enableOkButton() {
if (strings.isFalsyOrWhitespace(this._selectedFilePath) || this._isFolderSelected === true) { if (strings.isFalsyOrWhitespace(this._selectedFilePath) || (this._isFolderSelected && !this._viewModel.showFoldersOnly)) {
this._okButton.enabled = false; this._okButton.enabled = false;
} else { } else {
this._okButton.enabled = true; this._okButton.enabled = true;
@@ -215,9 +226,11 @@ export class FileBrowserDialog extends Modal {
} }
private registerListeners(): void { private registerListeners(): void {
this._register(this._fileFilterSelectBox.onDidSelect(selectData => { if (this._fileFilterSelectBox) {
this.onFilterSelectChanged(selectData.index).catch(err => onUnexpectedError(err)); this._register(this._fileFilterSelectBox.onDidSelect(selectData => {
})); this.onFilterSelectChanged(selectData.index).catch(err => onUnexpectedError(err));
}));
}
this._register(this._filePathInputBox.onDidChange(e => { this._register(this._filePathInputBox.onDidChange(e => {
this.onFilePathChange(e); this.onFilePathChange(e);
})); }));

View File

@@ -25,7 +25,8 @@ export class FileBrowserDialogController implements IFileBrowserDialogController
fileFilters: [{ label: string, filters: string[] }], fileFilters: [{ label: string, filters: string[] }],
fileValidationServiceType: string, fileValidationServiceType: string,
isWide: boolean, isWide: boolean,
handleOnClosed: (path: string | undefined) => void handleOnClosed: (path: string | undefined) => void,
showFoldersOnly?: boolean
): void { ): void {
if (!this._fileBrowserDialog) { if (!this._fileBrowserDialog) {
this._fileBrowserDialog = this._instantiationService.createInstance(FileBrowserDialog, localize('filebrowser.selectFile', "Select a file")); this._fileBrowserDialog = this._instantiationService.createInstance(FileBrowserDialog, localize('filebrowser.selectFile', "Select a file"));
@@ -44,6 +45,6 @@ export class FileBrowserDialogController implements IFileBrowserDialogController
this._fileBrowserDialog = undefined; this._fileBrowserDialog = undefined;
}); });
this._fileBrowserDialog.open(ownerUri, expandPath, fileFilters, fileValidationServiceType); this._fileBrowserDialog.open(ownerUri, expandPath, fileFilters, fileValidationServiceType, showFoldersOnly);
} }
} }

View File

@@ -9,10 +9,12 @@
padding-left: 12px; padding-left: 12px;
padding-right: 12px; padding-right: 12px;
box-sizing: border-box; box-sizing: border-box;
display: flex;
flex-flow: column;
} }
.file-browser-dialog .tree-view { .file-browser-dialog .tree-view {
height: calc(100% - 90px); flex: 1 1 auto;
} }
.file-table-content { .file-table-content {
@@ -21,8 +23,8 @@
.file-browser-dialog .option-section { .file-browser-dialog .option-section {
padding-top: 10px; padding-top: 10px;
height: 90px;
box-sizing: border-box; box-sizing: border-box;
flex: 0 1 auto;
} }
.file-input-label { .file-input-label {

View File

@@ -16,5 +16,6 @@ export interface IFileBrowserDialogController {
fileFilters: { label: string, filters: string[] }[], fileFilters: { label: string, filters: string[] }[],
fileValidationServiceType: string, fileValidationServiceType: string,
isWide: boolean, isWide: boolean,
handleOnOk: (path: string | undefined) => void): void; handleOnOk: (path: string | undefined) => void,
showFoldersOnly?: boolean): void;
} }

View File

@@ -46,11 +46,11 @@ export class FileBrowserService implements IFileBrowserService {
return this._onPathValidate.event; return this._onPathValidate.event;
} }
public openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Promise<boolean> { public openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean, showFoldersOnly?: boolean): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => { return new Promise<boolean>((resolve, reject) => {
const provider = this.getProvider(ownerUri); const provider = this.getProvider(ownerUri);
if (provider) { if (provider) {
provider.openFileBrowser(ownerUri, expandPath, fileFilters, changeFilter).then(result => { provider.openFileBrowser(ownerUri, expandPath, fileFilters, changeFilter, showFoldersOnly).then(result => {
resolve(result); resolve(result);
}, error => { }, error => {
reject(error); reject(error);

View File

@@ -15,6 +15,7 @@ export class FileBrowserViewModel {
private _expandPath: string; private _expandPath: string;
private _fileFilters: [{ label: string, filters: string[] }]; private _fileFilters: [{ label: string, filters: string[] }];
private _fileValidationServiceType: string; private _fileValidationServiceType: string;
private _showFoldersOnly: boolean;
public formattedFileFilters: string[]; public formattedFileFilters: string[];
constructor(@IFileBrowserService private _fileBrowserService: IFileBrowserService) { constructor(@IFileBrowserService private _fileBrowserService: IFileBrowserService) {
@@ -28,14 +29,20 @@ export class FileBrowserViewModel {
this._fileBrowserService.onPathValidate(args => onPathValidateCallback(args)); this._fileBrowserService.onPathValidate(args => onPathValidateCallback(args));
} }
public get showFoldersOnly(): boolean {
return this._showFoldersOnly;
}
public initialize(ownerUri: string, public initialize(ownerUri: string,
expandPath: string, expandPath: string,
fileFilters: [{ label: string, filters: string[] }], fileFilters: [{ label: string, filters: string[] }],
fileValidationServiceType: string, fileValidationServiceType: string,
showFoldersOnly?: boolean
) { ) {
this._ownerUri = ownerUri; this._ownerUri = ownerUri;
this._expandPath = expandPath; this._expandPath = expandPath;
this._fileValidationServiceType = fileValidationServiceType; this._fileValidationServiceType = fileValidationServiceType;
this._showFoldersOnly = !!showFoldersOnly;
if (!fileFilters) { if (!fileFilters) {
this._fileFilters = [{ label: localize('allFiles', "All files"), filters: ['*'] }]; this._fileFilters = [{ label: localize('allFiles', "All files"), filters: ['*'] }];
@@ -55,7 +62,7 @@ export class FileBrowserViewModel {
public async openFileBrowser(filterIndex: number, changeFilter: boolean): Promise<void> { public async openFileBrowser(filterIndex: number, changeFilter: boolean): Promise<void> {
if (this._fileFilters[filterIndex]) { if (this._fileFilters[filterIndex]) {
await this._fileBrowserService.openFileBrowser(this._ownerUri, this._expandPath, this._fileFilters[filterIndex].filters, changeFilter); await this._fileBrowserService.openFileBrowser(this._ownerUri, this._expandPath, this._fileFilters[filterIndex].filters, changeFilter, this._showFoldersOnly);
} }
} }

View File

@@ -24,7 +24,7 @@ export interface IFileBrowserService {
/** /**
* Open file browser * Open file browser
*/ */
openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Promise<boolean>; openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean, showFoldersOnly?: boolean): Promise<boolean>;
/** /**
* Event called when file browser is opened * Event called when file browser is opened