mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 02:48:30 -05:00
Introduce Trust Book in Book Viewlet (#9414)
This commit is contained in:
@@ -41,6 +41,14 @@
|
||||
"type": "number",
|
||||
"default": 5000,
|
||||
"description": "%notebook.maxTableRows.description%"
|
||||
},
|
||||
"notebook.trustedBooks":{
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"description": "%notebook.trustedBooks.description%",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -159,6 +167,15 @@
|
||||
"light": "resources/light/save.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.trustBook",
|
||||
"title": "%title.trustBook%",
|
||||
"category": "%books-preview-category%",
|
||||
"icon": {
|
||||
"dark": "resources/dark/trust_inverse.svg",
|
||||
"light": "resources/light/trust.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.searchBook",
|
||||
"title": "%title.searchJupyterBook%",
|
||||
@@ -292,6 +309,10 @@
|
||||
"command": "notebook.command.searchUntitledBook",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.trustBook",
|
||||
"when": "view == bookTreeView && viewItem == savedBook"
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.closeBook",
|
||||
"when": "false"
|
||||
@@ -326,6 +347,11 @@
|
||||
}
|
||||
],
|
||||
"view/item/context": [
|
||||
{
|
||||
"command": "notebook.command.trustBook",
|
||||
"when": "view == bookTreeView && viewItem == savedBook",
|
||||
"group": "inline"
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.searchBook",
|
||||
"when": "view == bookTreeView && viewItem == savedBook || viewItem == section",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"notebook.useExistingPython.description": "Local path to a preexisting python installation used by Notebooks.",
|
||||
"notebook.overrideEditorTheming.description": "Override editor default settings in the Notebook editor. Settings include background color, current line color and border",
|
||||
"notebook.maxTableRows.description": "Maximum number of rows returned per table in the Notebook editor",
|
||||
"notebook.trustedBooks.description": "Notebooks contained in these books will automatically be trusted.",
|
||||
"notebook.maxBookSearchDepth.description": "Maximum depth of subdirectories to search for Books (Enter 0 for infinite)",
|
||||
"notebook.command.new": "New Notebook",
|
||||
"notebook.command.open": "Open Notebook",
|
||||
@@ -30,6 +31,7 @@
|
||||
"title.SQL19PreviewBook": "SQL Server 2019 Guide",
|
||||
"books-preview-category": "Jupyter Books",
|
||||
"title.saveJupyterBook": "Save Book",
|
||||
"title.trustBook": "Trust Book",
|
||||
"title.searchJupyterBook": "Search Book",
|
||||
"title.SavedBooks": "Saved Books",
|
||||
"title.UnsavedBooks": "Unsaved Books",
|
||||
|
||||
3
extensions/notebook/resources/dark/trust_inverse.svg
Normal file
3
extensions/notebook/resources/dark/trust_inverse.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.26991 1.02742C8.02998 0.993516 7.777 0.976562 7.51099 0.976562C6.97897 0.976562 6.49127 1.04567 6.04791 1.1839C5.60456 1.32212 5.1612 1.53467 4.71784 1.82155C4.34229 2.06148 3.96674 2.25708 3.59119 2.40835C3.21564 2.55961 2.83488 2.67697 2.4489 2.76042C2.06292 2.84388 1.6665 2.90125 1.25966 2.93255C0.852811 2.96385 0.432925 2.97949 0 2.97949V6.98536C0 7.74689 0.0951916 8.4693 0.285574 9.15259C0.475954 9.83589 0.739362 10.484 1.07579 11.0968C1.41223 11.7097 1.80994 12.2874 2.26895 12.8299C2.72795 13.3723 3.22607 13.8822 3.76332 14.3594C4.30056 14.8367 4.8678 15.2814 5.46503 15.6934C6.06226 16.1055 6.6634 16.4862 7.26845 16.8357L7.51099 16.9765L7.75353 16.8357C8.35859 16.4862 8.95972 16.1055 9.55695 15.6934C10.1542 15.2814 10.7214 14.8367 11.2587 14.3594C11.7959 13.8822 12.294 13.3723 12.753 12.8299C13.212 12.2874 13.6098 11.7097 13.9462 11.0968C14.2826 10.484 14.546 9.83589 14.7364 9.15259C14.9268 8.4693 15.022 7.74689 15.022 6.98536V2.97949C14.5891 2.97949 14.1692 2.96385 13.7623 2.93255C13.3555 2.90125 12.9591 2.84388 12.5731 2.76042C12.1871 2.67697 11.8063 2.55961 11.4308 2.40835C11.0552 2.25708 10.6797 2.06149 10.3041 1.82155C10.0799 1.68072 9.85817 1.55684 9.6391 1.44991C9.42003 1.34299 9.19705 1.25431 8.97016 1.1839C8.74326 1.11348 8.50985 1.06132 8.26991 1.02742ZM11.7868 3.58976C12.4988 3.7984 13.2433 3.92358 14.0205 3.96531L14.0205 6.98536C14.0205 7.64778 13.9331 8.28413 13.7584 8.8944C13.5837 9.50467 13.345 10.0863 13.0425 10.6391C12.74 11.192 12.3801 11.7149 11.9628 12.2078C11.5455 12.7008 11.097 13.1637 10.6171 13.5966C10.1372 14.0295 9.63259 14.4312 9.10317 14.8015C8.57375 15.1718 8.04302 15.5109 7.51099 15.8186C6.97897 15.5109 6.44824 15.1718 5.91882 14.8015C5.3894 14.4312 4.88476 14.0295 4.40489 13.5966C3.92502 13.1637 3.47645 12.7008 3.05917 12.2078C2.64189 11.7149 2.28199 11.192 1.97946 10.6391C1.67694 10.0863 1.43831 9.50467 1.26357 8.8944C1.08884 8.28413 1.00147 7.64778 1.00147 6.98536V3.96531C1.77865 3.92358 2.52323 3.7984 3.23521 3.58976C3.94719 3.38112 4.63178 3.06556 5.28899 2.64306C5.64368 2.41356 5.99706 2.24535 6.34914 2.13842C6.70122 2.03149 7.0885 1.97803 7.51099 1.97803C7.93348 1.97803 8.32077 2.03149 8.67285 2.13842C9.02493 2.24535 9.3783 2.41356 9.73299 2.64306C10.3902 3.06556 11.0748 3.38112 11.7868 3.58976Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
3
extensions/notebook/resources/light/trust.svg
Normal file
3
extensions/notebook/resources/light/trust.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.26991 1.02742C8.02998 0.993516 7.777 0.976562 7.51099 0.976562C6.97897 0.976562 6.49127 1.04567 6.04791 1.1839C5.60456 1.32212 5.1612 1.53467 4.71784 1.82155C4.34229 2.06148 3.96674 2.25708 3.59119 2.40835C3.21564 2.55961 2.83488 2.67697 2.4489 2.76042C2.06292 2.84388 1.6665 2.90125 1.25966 2.93255C0.852811 2.96385 0.432925 2.97949 0 2.97949V6.98536C0 7.74689 0.0951916 8.4693 0.285574 9.15259C0.475954 9.83589 0.739362 10.484 1.07579 11.0968C1.41223 11.7097 1.80994 12.2874 2.26895 12.8299C2.72795 13.3723 3.22607 13.8822 3.76332 14.3594C4.30056 14.8367 4.8678 15.2814 5.46503 15.6934C6.06226 16.1055 6.6634 16.4862 7.26845 16.8357L7.51099 16.9765L7.75353 16.8357C8.35859 16.4862 8.95972 16.1055 9.55695 15.6934C10.1542 15.2814 10.7214 14.8367 11.2587 14.3594C11.7959 13.8822 12.294 13.3723 12.753 12.8299C13.212 12.2874 13.6098 11.7097 13.9462 11.0968C14.2826 10.484 14.546 9.83589 14.7364 9.15259C14.9268 8.4693 15.022 7.74689 15.022 6.98536V2.97949C14.5891 2.97949 14.1692 2.96385 13.7623 2.93255C13.3555 2.90125 12.9591 2.84388 12.5731 2.76042C12.1871 2.67697 11.8063 2.55961 11.4308 2.40835C11.0552 2.25708 10.6797 2.06149 10.3041 1.82155C10.0799 1.68072 9.85817 1.55684 9.6391 1.44991C9.42003 1.34299 9.19705 1.25431 8.97016 1.1839C8.74326 1.11348 8.50985 1.06132 8.26991 1.02742ZM11.7868 3.58976C12.4988 3.7984 13.2433 3.92358 14.0205 3.96531L14.0205 6.98536C14.0205 7.64778 13.9331 8.28413 13.7584 8.8944C13.5837 9.50467 13.345 10.0863 13.0425 10.6391C12.74 11.192 12.3801 11.7149 11.9628 12.2078C11.5455 12.7008 11.097 13.1637 10.6171 13.5966C10.1372 14.0295 9.63259 14.4312 9.10317 14.8015C8.57375 15.1718 8.04302 15.5109 7.51099 15.8186C6.97897 15.5109 6.44824 15.1718 5.91882 14.8015C5.3894 14.4312 4.88476 14.0295 4.40489 13.5966C3.92502 13.1637 3.47645 12.7008 3.05917 12.2078C2.64189 11.7149 2.28199 11.192 1.97946 10.6391C1.67694 10.0863 1.43831 9.50467 1.26357 8.8944C1.08884 8.28413 1.00147 7.64778 1.00147 6.98536V3.96531C1.77865 3.92358 2.52323 3.7984 3.23521 3.58976C3.94719 3.38112 4.63178 3.06556 5.28899 2.64306C5.64368 2.41356 5.99706 2.24535 6.34914 2.13842C6.70122 2.03149 7.0885 1.97803 7.51099 1.97803C7.93348 1.97803 8.32077 2.03149 8.67285 2.13842C9.02493 2.24535 9.3783 2.41356 9.73299 2.64306C10.3902 3.06556 11.0748 3.38112 11.7868 3.58976Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -7,19 +7,20 @@ import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as constants from '../common/constants';
|
||||
import * as fsw from 'fs';
|
||||
import { IPrompter, QuestionTypes, IQuestion } from '../prompts/question';
|
||||
import CodeAdapter from '../prompts/adapter';
|
||||
import { BookTreeItem } from './bookTreeItem';
|
||||
import { BookModel } from './bookModel';
|
||||
import { Deferred } from '../common/promise';
|
||||
import { IBookTrustManager, BookTrustManager } from './bookTrustManager';
|
||||
import * as loc from '../common/localizedConstants';
|
||||
import { ApiWrapper } from '../common/apiWrapper';
|
||||
|
||||
const Content = 'content';
|
||||
|
||||
export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeItem> {
|
||||
|
||||
private _onDidChangeTreeData: vscode.EventEmitter<BookTreeItem | undefined> = new vscode.EventEmitter<BookTreeItem | undefined>();
|
||||
readonly onDidChangeTreeData: vscode.Event<BookTreeItem | undefined> = this._onDidChangeTreeData.event;
|
||||
private _throttleTimer: any;
|
||||
@@ -27,21 +28,22 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
private _extensionContext: vscode.ExtensionContext;
|
||||
private prompter: IPrompter;
|
||||
private _initializeDeferred: Deferred<void> = new Deferred<void>();
|
||||
private _bookViewer: vscode.TreeView<BookTreeItem>;
|
||||
private _openAsUntitled: boolean;
|
||||
private _apiWrapper: ApiWrapper;
|
||||
private _bookTrustManager: IBookTrustManager;
|
||||
|
||||
private _bookViewer: vscode.TreeView<BookTreeItem>;
|
||||
public viewId: string;
|
||||
public books: BookModel[];
|
||||
public currentBook: BookModel;
|
||||
|
||||
constructor(apiWrapper: ApiWrapper, workspaceFolders: vscode.WorkspaceFolder[], extensionContext: vscode.ExtensionContext, openAsUntitled: boolean, view: string) {
|
||||
constructor(private _apiWrapper: ApiWrapper, workspaceFolders: vscode.WorkspaceFolder[], extensionContext: vscode.ExtensionContext, openAsUntitled: boolean, view: string) {
|
||||
this._openAsUntitled = openAsUntitled;
|
||||
this._extensionContext = extensionContext;
|
||||
this.books = [];
|
||||
this.initialize(workspaceFolders).catch(e => console.error(e));
|
||||
this.viewId = view;
|
||||
this.prompter = new CodeAdapter();
|
||||
this._apiWrapper = apiWrapper ? apiWrapper : new ApiWrapper();
|
||||
this._bookTrustManager = new BookTrustManager(this.books, _apiWrapper);
|
||||
}
|
||||
|
||||
private async initialize(workspaceFolders: vscode.WorkspaceFolder[]): Promise<void> {
|
||||
@@ -60,6 +62,28 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
return this._initializeDeferred.promise;
|
||||
}
|
||||
|
||||
get _visitedNotebooks(): string[] {
|
||||
return this._extensionContext.globalState.get(constants.visitedNotebooksMementoKey, []);
|
||||
}
|
||||
|
||||
set _visitedNotebooks(value: string[]) {
|
||||
this._extensionContext.globalState.update(constants.visitedNotebooksMementoKey, value);
|
||||
}
|
||||
|
||||
trustBook(bookTreeItem?: BookTreeItem): void {
|
||||
let bookPathToTrust = bookTreeItem ? bookTreeItem.root : this.currentBook?.bookPath;
|
||||
|
||||
if (bookPathToTrust) {
|
||||
let trustChanged = this._bookTrustManager.setBookAsTrusted(bookPathToTrust);
|
||||
|
||||
if (trustChanged) {
|
||||
this._apiWrapper.showInfoMessage(loc.msgBookTrusted);
|
||||
} else {
|
||||
this._apiWrapper.showInfoMessage(loc.msgBookAlreadyTrusted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async openBook(bookPath: string, urlToOpen?: string): Promise<void> {
|
||||
try {
|
||||
let books: BookModel[] = this.books.filter(book => book.bookPath === bookPath) || [];
|
||||
@@ -157,6 +181,19 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
if (this._openAsUntitled) {
|
||||
this.openNotebookAsUntitled(resource);
|
||||
} else {
|
||||
// let us keep a list of already visited notebooks so that we do not trust them again, potentially
|
||||
// overriding user changes
|
||||
let normalizedResource = path.normalize(resource);
|
||||
|
||||
if (this._visitedNotebooks.indexOf(normalizedResource) === -1
|
||||
&& this._bookTrustManager.isNotebookTrustedByDefault(normalizedResource)) {
|
||||
let openDocumentListenerUnsubscriber = azdata.nb.onDidOpenNotebookDocument((document: azdata.nb.NotebookDocument) => {
|
||||
document.setTrusted(true);
|
||||
this._visitedNotebooks = this._visitedNotebooks.concat([normalizedResource]);
|
||||
openDocumentListenerUnsubscriber.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
let doc = await vscode.workspace.openTextDocument(resource);
|
||||
vscode.window.showTextDocument(doc);
|
||||
}
|
||||
@@ -377,6 +414,4 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
default: false
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
114
extensions/notebook/src/book/bookTrustManager.ts
Normal file
114
extensions/notebook/src/book/bookTrustManager.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import * as constants from './../common/constants';
|
||||
import { BookTreeItem } from './bookTreeItem';
|
||||
import { BookModel } from './bookModel';
|
||||
import { ApiWrapper } from '../common/apiWrapper';
|
||||
|
||||
export interface IBookTrustManager {
|
||||
isNotebookTrustedByDefault(notebookUri: string): boolean;
|
||||
setBookAsTrusted(bookRootPath: string): boolean;
|
||||
}
|
||||
|
||||
enum TrustBookOperation {
|
||||
Add,
|
||||
Remove
|
||||
}
|
||||
|
||||
export class BookTrustManager implements IBookTrustManager {
|
||||
constructor(private books: BookModel[], private apiWrapper: ApiWrapper) { }
|
||||
|
||||
isNotebookTrustedByDefault(notebookUri: string): boolean {
|
||||
let normalizedNotebookUri: string = path.normalize(notebookUri);
|
||||
let treeBookItems: BookTreeItem[] = this.getBookTreeItems();
|
||||
let trustableBookPaths = this.getTrustableBookPaths();
|
||||
let hasTrustedBookPath: boolean = treeBookItems
|
||||
.filter(bookItem => trustableBookPaths.some(trustableBookPath => trustableBookPath === path.join(bookItem.book.root, path.sep)))
|
||||
.some(bookItem => normalizedNotebookUri.startsWith(path.join(bookItem.root, 'content', path.sep)));
|
||||
let isNotebookTrusted = hasTrustedBookPath && this.books.some(bookModel => bookModel.getNotebook(normalizedNotebookUri));
|
||||
return isNotebookTrusted;
|
||||
}
|
||||
|
||||
getTrustableBookPaths() {
|
||||
let trustablePaths: string[];
|
||||
let bookPathsInConfig: string[] = this.getTrustedBookPathsInConfig();
|
||||
|
||||
if (this.hasWorkspaceFolders()) {
|
||||
let workspaceFolders = this.apiWrapper.getWorkspaceFolders();
|
||||
|
||||
trustablePaths = bookPathsInConfig
|
||||
.map(trustableBookPath => workspaceFolders
|
||||
.map(workspaceFolder => path.join(workspaceFolder.uri.fsPath, trustableBookPath)))
|
||||
.reduce((accumulator, currentTrustableBookPaths) => accumulator.concat(currentTrustableBookPaths), []);
|
||||
|
||||
} else {
|
||||
trustablePaths = bookPathsInConfig;
|
||||
}
|
||||
|
||||
return trustablePaths;
|
||||
}
|
||||
|
||||
getBookTreeItems(): BookTreeItem[] {
|
||||
return this.books
|
||||
.map(book => book.bookItems) // select all the books
|
||||
.reduce((accumulator, currentBookItemList) => accumulator.concat(currentBookItemList), []);
|
||||
}
|
||||
|
||||
setBookAsTrusted(bookRootPath: string): boolean {
|
||||
return this.updateTrustedBooks(bookRootPath, TrustBookOperation.Add);
|
||||
}
|
||||
|
||||
getTrustedBookPathsInConfig(): string[] {
|
||||
let config: vscode.WorkspaceConfiguration = this.apiWrapper.getConfiguration(constants.notebookConfigKey);
|
||||
let trustedBookDirectories: string[] = config.get(constants.trustedBooksConfigKey);
|
||||
|
||||
return trustedBookDirectories;
|
||||
}
|
||||
|
||||
setTrustedBookPathsInConfig(bookPaths: string[]) {
|
||||
let config: vscode.WorkspaceConfiguration = this.apiWrapper.getConfiguration(constants.notebookConfigKey);
|
||||
let storeInWorspace: boolean = this.hasWorkspaceFolders();
|
||||
|
||||
config.update(constants.trustedBooksConfigKey, bookPaths, storeInWorspace ? false : vscode.ConfigurationTarget.Global);
|
||||
}
|
||||
|
||||
hasWorkspaceFolders(): boolean {
|
||||
let workspaceFolders = this.apiWrapper.getWorkspaceFolders();
|
||||
return workspaceFolders && workspaceFolders.length > 0;
|
||||
}
|
||||
|
||||
updateTrustedBooks(bookPath: string, operation: TrustBookOperation) {
|
||||
let modifiedTrustedBooks = false;
|
||||
let bookPathToChange: string = path.join(bookPath, path.sep);
|
||||
|
||||
if (this.hasWorkspaceFolders()) {
|
||||
let workspaceFolders = this.apiWrapper.getWorkspaceFolders();
|
||||
let matchingWorkspaceFolder: vscode.WorkspaceFolder = workspaceFolders
|
||||
.find(ws => bookPathToChange.startsWith(path.normalize(ws.uri.fsPath)));
|
||||
|
||||
// if notebook is stored in a workspace folder, then store only the relative directory
|
||||
if (matchingWorkspaceFolder) {
|
||||
bookPathToChange = bookPathToChange.replace(path.normalize(matchingWorkspaceFolder.uri.fsPath), '');
|
||||
}
|
||||
}
|
||||
|
||||
let trustedBooks: string[] = this.getTrustedBookPathsInConfig();
|
||||
let existingBookIndex = trustedBooks.map(trustedBookPath => path.normalize(trustedBookPath)).indexOf(bookPathToChange);
|
||||
|
||||
if (existingBookIndex !== -1 && operation === TrustBookOperation.Remove) {
|
||||
trustedBooks.splice(existingBookIndex, 1);
|
||||
modifiedTrustedBooks = true;
|
||||
} else if (existingBookIndex === -1 && operation === TrustBookOperation.Add) {
|
||||
trustedBooks.push(bookPathToChange);
|
||||
modifiedTrustedBooks = true;
|
||||
}
|
||||
|
||||
this.setTrustedBookPathsInConfig(trustedBooks);
|
||||
|
||||
return modifiedTrustedBooks;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,10 @@ import { CommandContext, BuiltInCommands } from './constants';
|
||||
* this API from our code
|
||||
*/
|
||||
export class ApiWrapper {
|
||||
public getWorkspaceFolders(): vscode.WorkspaceFolder[] {
|
||||
return [].concat(vscode.workspace.workspaceFolders || []);
|
||||
}
|
||||
|
||||
public createOutputChannel(name: string): vscode.OutputChannel {
|
||||
return vscode.window.createOutputChannel(name);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export const pythonVersion = '3.6.6';
|
||||
export const pythonPathConfigKey = 'pythonPath';
|
||||
export const existingPythonConfigKey = 'useExistingPython';
|
||||
export const notebookConfigKey = 'notebook';
|
||||
export const trustedBooksConfigKey = 'trustedBooks';
|
||||
export const maxBookSearchDepth = 'maxBookSearchDepth';
|
||||
|
||||
export const winPlatform = 'win32';
|
||||
@@ -33,6 +34,8 @@ export const localhostName = 'localhost';
|
||||
export const localhostTitle = localize('managePackages.localhost', "localhost");
|
||||
export const PackageNotFoundError = localize('managePackages.packageNotFound', "Could not find the specified package");
|
||||
|
||||
export const visitedNotebooksMementoKey = 'notebooks.visited';
|
||||
|
||||
export enum BuiltInCommands {
|
||||
SetContext = 'setContext'
|
||||
}
|
||||
|
||||
@@ -21,8 +21,12 @@ export const confirmReplace = localize('confirmReplace', "Folder already exists.
|
||||
export const openNotebookCommand = localize('openNotebookCommand', "Open Notebook");
|
||||
export const openMarkdownCommand = localize('openMarkdownCommand', "Open Markdown");
|
||||
export const openExternalLinkCommand = localize('openExternalLinkCommand', "Open External Link");
|
||||
|
||||
export const msgBookTrusted = localize('msgBookTrusted', "Book is now trusted in the workspace.");
|
||||
export const msgBookAlreadyTrusted = localize('msgBookAlreadyTrusted', "Book is already trusted in this workspace.");
|
||||
export const msgBookUntrusted = localize('msgBookUntrusted', "Book is no longer trusted in this workspace");
|
||||
export const msgBookAlreadyUntrusted = localize('msgBookAlreadyUntrusted', "Book is already untrusted in this workspace.");
|
||||
export const missingTocError = localize('bookInitializeFailed', "Failed to find a toc.yml.");
|
||||
|
||||
export function missingFileError(title: string): string { return localize('missingFileError', "Missing file : {0}", title); }
|
||||
export function invalidTocFileError(): string { return localize('InvalidError.tocFile', "Invalid toc file"); }
|
||||
export function invalidTocError(title: string): string { return localize('Invalid toc.yml', "Error: {0} has an incorrect toc.yml file", title); }
|
||||
|
||||
@@ -36,6 +36,7 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openMarkdown', (resource) => bookTreeViewProvider.openMarkdown(resource)));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openExternalLink', (resource) => bookTreeViewProvider.openExternalLink(resource)));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.saveBook', () => untitledBookTreeViewProvider.saveJupyterBooks()));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.trustBook', (resource) => bookTreeViewProvider.trustBook(resource)));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.searchBook', (item) => bookTreeViewProvider.searchJupyterBooks(item)));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.searchUntitledBook', () => untitledBookTreeViewProvider.searchJupyterBooks()));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.openBook', () => bookTreeViewProvider.openNewBook()));
|
||||
|
||||
328
extensions/notebook/src/test/book/bookTrustManager.test.ts
Normal file
328
extensions/notebook/src/test/book/bookTrustManager.test.ts
Normal file
@@ -0,0 +1,328 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as should from 'should';
|
||||
import * as path from 'path';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as constants from '../../common/constants';
|
||||
import { IBookTrustManager, BookTrustManager } from '../../book/bookTrustManager';
|
||||
import { BookTreeItem, BookTreeItemFormat, BookTreeItemType } from '../../book/bookTreeItem';
|
||||
import { ApiWrapper } from '../../common/apiWrapper';
|
||||
import { WorkspaceConfiguration, ConfigurationTarget } from 'vscode';
|
||||
import { BookModel } from '../../book/bookModel';
|
||||
|
||||
describe('BookTrustManagerTests', function () {
|
||||
|
||||
describe('TrustingInWorkspaces', () => {
|
||||
let bookTrustManager: IBookTrustManager;
|
||||
let trustedSubFolders: string[];
|
||||
let books: BookModel[];
|
||||
|
||||
beforeEach(() => {
|
||||
trustedSubFolders = ['/SubFolder/'];
|
||||
|
||||
// Mock Workspace Configuration
|
||||
let workspaceConfigurtionMock: TypeMoq.IMock<WorkspaceConfiguration> = TypeMoq.Mock.ofType<WorkspaceConfiguration>();
|
||||
workspaceConfigurtionMock.setup(config => config.get(TypeMoq.It.isValue(constants.trustedBooksConfigKey))).returns(() => [].concat(trustedSubFolders));
|
||||
workspaceConfigurtionMock.setup(config => config.update(TypeMoq.It.isValue(constants.trustedBooksConfigKey), TypeMoq.It.isAny(), TypeMoq.It.isValue(false))).returns((key: string, newValues: string[]) => {
|
||||
trustedSubFolders.splice(0, trustedSubFolders.length, ...newValues); // Replace
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
// Mock Api Wrapper
|
||||
let apiWrapperMock: TypeMoq.IMock<ApiWrapper> = TypeMoq.Mock.ofType<ApiWrapper>();
|
||||
|
||||
apiWrapperMock.setup(api => api.getWorkspaceFolders()).returns(() => [
|
||||
{
|
||||
// @ts-ignore - Don't need all URI properties for this tests
|
||||
uri: {
|
||||
fsPath: '/temp/'
|
||||
},
|
||||
},
|
||||
{
|
||||
// @ts-ignore - Don't need all URI properties for this tests
|
||||
uri: {
|
||||
fsPath: '/temp2/'
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
apiWrapperMock.setup(api => api.getConfiguration(TypeMoq.It.isValue(constants.notebookConfigKey))).returns(() => workspaceConfigurtionMock.object);
|
||||
|
||||
// Mock Book Data
|
||||
let bookTreeItemFormat1: BookTreeItemFormat = {
|
||||
root: '/temp/SubFolder/',
|
||||
tableOfContents: {
|
||||
sections: [
|
||||
{
|
||||
url: path.join(path.sep, 'sample', 'notebook')
|
||||
},
|
||||
{
|
||||
url: path.join(path.sep, 'sample', 'notebook2')
|
||||
}
|
||||
]
|
||||
},
|
||||
isUntitled: undefined,
|
||||
page: undefined,
|
||||
title: undefined,
|
||||
treeItemCollapsibleState: undefined,
|
||||
type: BookTreeItemType.Book
|
||||
};
|
||||
|
||||
let bookTreeItemFormat2: BookTreeItemFormat = {
|
||||
root: '/temp/SubFolder2/',
|
||||
tableOfContents: {
|
||||
sections: [
|
||||
{
|
||||
url: path.join(path.sep, 'sample', 'notebook')
|
||||
}
|
||||
]
|
||||
},
|
||||
isUntitled: undefined,
|
||||
page: undefined,
|
||||
title: undefined,
|
||||
treeItemCollapsibleState: undefined,
|
||||
type: BookTreeItemType.Book
|
||||
};
|
||||
|
||||
let bookTreeItemFormat3: BookTreeItemFormat = {
|
||||
root: '/temp2/SubFolder3/',
|
||||
tableOfContents: {
|
||||
sections: [
|
||||
{
|
||||
url: path.join(path.sep, 'sample', 'notebook')
|
||||
}
|
||||
]
|
||||
},
|
||||
isUntitled: undefined,
|
||||
page: undefined,
|
||||
title: undefined,
|
||||
treeItemCollapsibleState: undefined,
|
||||
type: BookTreeItemType.Book
|
||||
};
|
||||
|
||||
let bookModel1Mock: TypeMoq.IMock<BookModel> = TypeMoq.Mock.ofType<BookModel>();
|
||||
bookModel1Mock.setup(model => model.bookItems).returns(() => [new BookTreeItem(bookTreeItemFormat1, undefined), new BookTreeItem(bookTreeItemFormat2, undefined)]);
|
||||
bookModel1Mock.setup(model => model.getNotebook(TypeMoq.It.isValue(path.join(path.sep,'temp','SubFolder','content','sample', 'notebook.ipynb')))).returns((uri: string) => TypeMoq.Mock.ofType<BookTreeItem>().object);
|
||||
bookModel1Mock.setup(model => model.getNotebook(TypeMoq.It.isValue(path.join(path.sep,'temp','SubFolder','content','sample', 'notebook2.ipynb')))).returns((uri: string) => TypeMoq.Mock.ofType<BookTreeItem>().object);
|
||||
bookModel1Mock.setup(model => model.getNotebook(TypeMoq.It.isValue(path.join(path.sep,'temp','SubFolder2','content','sample', 'notebook.ipynb')))).returns((uri: string) => TypeMoq.Mock.ofType<BookTreeItem>().object);
|
||||
bookModel1Mock.setup(model => model.getNotebook(TypeMoq.It.isAnyString())).returns((uri: string) => undefined);
|
||||
|
||||
let bookModel2Mock: TypeMoq.IMock<BookModel> = TypeMoq.Mock.ofType<BookModel>();
|
||||
bookModel2Mock.setup(model => model.bookItems).returns(() => [new BookTreeItem(bookTreeItemFormat3, undefined)]);
|
||||
bookModel2Mock.setup(model => model.getNotebook(TypeMoq.It.isValue(path.join(path.sep,'temp2','SubFolder','content','sample','notebook.ipynb')))).returns((uri: string) => TypeMoq.Mock.ofType<BookTreeItem>().object);
|
||||
bookModel2Mock.setup(model => model.getNotebook(TypeMoq.It.isAnyString())).returns((uri: string) => undefined);
|
||||
|
||||
books = [bookModel1Mock.object, bookModel2Mock.object];
|
||||
|
||||
bookTrustManager = new BookTrustManager(books, apiWrapperMock.object);
|
||||
});
|
||||
|
||||
it('should trust notebooks in a trusted book within a workspace', async () => {
|
||||
let notebookUri1 = path.join(path.sep,'temp','SubFolder','content','sample', 'notebook.ipynb');
|
||||
let notebookUri2 = path.join(path.sep,'temp','SubFolder','content','sample', 'notebook2.ipynb');
|
||||
|
||||
let isNotebook1Trusted = bookTrustManager.isNotebookTrustedByDefault(notebookUri1);
|
||||
let isNotebook2Trusted = bookTrustManager.isNotebookTrustedByDefault(notebookUri2);
|
||||
|
||||
should(isNotebook1Trusted).be.true("Notebook 1 should be trusted");
|
||||
should(isNotebook2Trusted).be.true("Notebook 2 should be trusted");
|
||||
|
||||
});
|
||||
|
||||
it('should NOT trust a notebook in an untrusted book within a workspace', async () => {
|
||||
let notebookUri = path.join(path.sep,'temp','SubFolder2','content', 'sample', 'notebook.ipynb');
|
||||
let isNotebookTrusted = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrusted).be.false("Notebook should be trusted");
|
||||
});
|
||||
|
||||
it('should trust notebook after book has been trusted within a workspace', async () => {
|
||||
let notebookUri = path.join(path.sep,'temp','SubFolder2','content', 'sample', 'notebook.ipynb');
|
||||
let isNotebookTrustedBeforeChange = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrustedBeforeChange).be.false("Notebook should NOT be trusted");
|
||||
|
||||
// add another book subfolder
|
||||
bookTrustManager.setBookAsTrusted('/SubFolder2/');
|
||||
|
||||
let isNotebookTrustedAfterChange = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrustedAfterChange).be.true("Notebook should be trusted");
|
||||
});
|
||||
|
||||
it('should NOT trust a notebook when untrusting a book within a workspace', async () => {
|
||||
let notebookUri = path.join(path.sep,'temp','SubFolder','content', 'sample', 'notebook.ipynb');
|
||||
let isNotebookTrusted = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrusted).be.true("Notebook should be trusted");
|
||||
|
||||
// remove trusted subfolders
|
||||
trustedSubFolders = [];
|
||||
|
||||
let isNotebookTrustedAfterChange = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrustedAfterChange).be.false("Notebook should not be trusted after book removal");
|
||||
});
|
||||
|
||||
it('should NOT trust an unknown book within a workspace', async () => {
|
||||
let notebookUri = path.join(path.sep, 'randomfolder', 'randomsubfolder', 'content', 'randomnotebook.ipynb');
|
||||
let isNotebookTrusted = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrusted).be.false("Random notebooks should not be trusted");
|
||||
});
|
||||
|
||||
it('should NOT trust notebook inside trusted subfolder when absent in table of contents ', async () => {
|
||||
bookTrustManager.setBookAsTrusted('/temp/SubFolder/');
|
||||
|
||||
let notebookUri = path.join(path.sep, 'temp', 'SubFolder', 'content', 'sample', 'notInToc.ipynb');
|
||||
let isNotebookTrusted = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrusted).be.false("Notebook should NOT be trusted");
|
||||
});
|
||||
});
|
||||
|
||||
describe('TrustingInFolder', () => {
|
||||
|
||||
let bookTrustManager: IBookTrustManager;
|
||||
let books: BookModel[];
|
||||
let trustedFolders: string[] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
// Mock Workspace Configuration
|
||||
let workspaceConfigurtionMock: TypeMoq.IMock<WorkspaceConfiguration> = TypeMoq.Mock.ofType<WorkspaceConfiguration>();
|
||||
workspaceConfigurtionMock.setup(config => config.get(TypeMoq.It.isValue(constants.trustedBooksConfigKey))).returns(() => [].concat(trustedFolders));
|
||||
workspaceConfigurtionMock.setup(config => config.update(TypeMoq.It.isValue(constants.trustedBooksConfigKey), TypeMoq.It.isAny(), TypeMoq.It.isValue(ConfigurationTarget.Global))).returns((key: string, newValues: string[], target: ConfigurationTarget) => {
|
||||
trustedFolders.splice(0, trustedFolders.length, ...newValues); // Replace
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
|
||||
// Mock Api Wrapper
|
||||
let apiWrapperMock: TypeMoq.IMock<ApiWrapper> = TypeMoq.Mock.ofType<ApiWrapper>();
|
||||
|
||||
apiWrapperMock.setup(api => api.getWorkspaceFolders()).returns(() => []);
|
||||
apiWrapperMock.setup(api => api.getConfiguration(TypeMoq.It.isValue(constants.notebookConfigKey))).returns(() => workspaceConfigurtionMock.object);
|
||||
let bookTreeItemFormat1: BookTreeItemFormat = {
|
||||
root: '/temp/SubFolder/',
|
||||
tableOfContents: {
|
||||
sections: [
|
||||
{
|
||||
url: path.join(path.sep, 'sample', 'notebook')
|
||||
},
|
||||
{
|
||||
url: path.join(path.sep, 'sample', 'notebook2')
|
||||
}
|
||||
]
|
||||
},
|
||||
isUntitled: undefined,
|
||||
page: undefined,
|
||||
title: undefined,
|
||||
treeItemCollapsibleState: undefined,
|
||||
type: BookTreeItemType.Book
|
||||
};
|
||||
|
||||
let bookTreeItemFormat2: BookTreeItemFormat = {
|
||||
root: '/temp/SubFolder2/',
|
||||
tableOfContents: {
|
||||
sections: [
|
||||
{
|
||||
url: path.join(path.sep, 'sample', 'notebook')
|
||||
},
|
||||
{
|
||||
url: path.join(path.sep, 'sample', 'notebook2')
|
||||
}
|
||||
]
|
||||
},
|
||||
isUntitled: undefined,
|
||||
page: undefined,
|
||||
title: undefined,
|
||||
treeItemCollapsibleState: undefined,
|
||||
type: BookTreeItemType.Book
|
||||
};
|
||||
|
||||
let bookModel1Mock: TypeMoq.IMock<BookModel> = TypeMoq.Mock.ofType<BookModel>();
|
||||
bookModel1Mock.setup(model => model.bookItems).returns(() => [new BookTreeItem(bookTreeItemFormat1, undefined)]);
|
||||
bookModel1Mock.setup(model => model.getNotebook(TypeMoq.It.isValue(path.join(path.sep,'temp','SubFolder','content', 'sample', 'notebook.ipynb')))).returns((uri: string) => TypeMoq.Mock.ofType<BookTreeItem>().object);
|
||||
bookModel1Mock.setup(model => model.getNotebook(TypeMoq.It.isValue(path.join(path.sep,'temp','SubFolder','content', 'sample', 'notebook2.ipynb')))).returns((uri: string) => TypeMoq.Mock.ofType<BookTreeItem>().object);
|
||||
bookModel1Mock.setup(model => model.getNotebook(TypeMoq.It.isAnyString())).returns((uri: string) => undefined);
|
||||
|
||||
let bookModel2Mock: TypeMoq.IMock<BookModel> = TypeMoq.Mock.ofType<BookModel>();
|
||||
bookModel2Mock.setup(model => model.bookItems).returns(() => [new BookTreeItem(bookTreeItemFormat2, undefined)]);
|
||||
bookModel2Mock.setup(model => model.getNotebook(TypeMoq.It.isValue(path.join(path.sep,'temp','SubFolder2','content', 'sample', 'notebook.ipynb')))).returns((uri: string) => TypeMoq.Mock.ofType<BookTreeItem>().object);
|
||||
bookModel2Mock.setup(model => model.getNotebook(TypeMoq.It.isValue(path.join(path.sep,'temp','SubFolder2','content', 'sample', 'notebook2.ipynb')))).returns((uri: string) => TypeMoq.Mock.ofType<BookTreeItem>().object);
|
||||
bookModel2Mock.setup(model => model.getNotebook(TypeMoq.It.isAnyString())).returns((uri: string) => undefined);
|
||||
|
||||
books = [bookModel1Mock.object, bookModel2Mock.object];
|
||||
|
||||
bookTrustManager = new BookTrustManager(books, apiWrapperMock.object);
|
||||
});
|
||||
|
||||
it('should trust notebooks in a trusted book in a folder', async () => {
|
||||
bookTrustManager.setBookAsTrusted('/temp/SubFolder/');
|
||||
|
||||
let notebookUri1 = path.join(path.sep,'temp','SubFolder','content', 'sample', 'notebook.ipynb');
|
||||
let notebookUri2 = path.join(path.sep,'temp','SubFolder','content', 'sample', 'notebook2.ipynb');
|
||||
|
||||
let isNotebook1Trusted = bookTrustManager.isNotebookTrustedByDefault(notebookUri1);
|
||||
let isNotebook2Trusted = bookTrustManager.isNotebookTrustedByDefault(notebookUri2);
|
||||
|
||||
should(isNotebook1Trusted).be.true("Notebook 1 should be trusted");
|
||||
should(isNotebook2Trusted).be.true("Notebook 2 should be trusted");
|
||||
|
||||
});
|
||||
|
||||
it('should NOT trust a notebook in an untrusted book in a folder', async () => {
|
||||
let notebookUri = path.join(path.sep,'temp','SubFolder2','content', 'sample', 'notebook.ipynb');
|
||||
let isNotebookTrusted = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrusted).be.false("Notebook should be trusted");
|
||||
});
|
||||
|
||||
it('should trust notebook after book has been added to a folder', async () => {
|
||||
let notebookUri = path.join(path.sep,'temp','SubFolder2','content', 'sample','notebook.ipynb');
|
||||
let isNotebookTrustedBeforeChange = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrustedBeforeChange).be.false("Notebook should NOT be trusted");
|
||||
|
||||
bookTrustManager.setBookAsTrusted('/temp/SubFolder2/');
|
||||
|
||||
let isNotebookTrustedAfterChange = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrustedAfterChange).be.true("Notebook should be trusted");
|
||||
});
|
||||
|
||||
it('should NOT trust a notebook when untrusting a book in folder', async () => {
|
||||
bookTrustManager.setBookAsTrusted('/temp/SubFolder/');
|
||||
let notebookUri = path.join(path.sep,'temp','SubFolder','content', 'sample', 'notebook.ipynb');
|
||||
let isNotebookTrusted = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrusted).be.true("Notebook should be trusted");
|
||||
|
||||
trustedFolders = [];
|
||||
|
||||
let isNotebookTrustedAfterChange = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrustedAfterChange).be.false("Notebook should not be trusted after book removal");
|
||||
});
|
||||
|
||||
it('should NOT trust an unknown book', async () => {
|
||||
let notebookUri = path.join(path.sep, 'randomfolder', 'randomsubfolder', 'content', 'randomnotebook.ipynb');
|
||||
let isNotebookTrusted = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrusted).be.false("Random notebooks should not be trusted");
|
||||
});
|
||||
|
||||
it('should NOT trust notebook inside trusted subfolder when absent in table of contents ', async () => {
|
||||
bookTrustManager.setBookAsTrusted('/temp/SubFolder/');
|
||||
|
||||
let notebookUri = path.join(path.sep, 'temp', 'SubFolder', 'content', 'sample', 'notInToc.ipynb');
|
||||
let isNotebookTrusted = bookTrustManager.isNotebookTrustedByDefault(notebookUri);
|
||||
|
||||
should(isNotebookTrusted).be.false("Notebook should NOT be trusted");
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
8
src/sql/azdata.proposed.d.ts
vendored
8
src/sql/azdata.proposed.d.ts
vendored
@@ -29,6 +29,14 @@ declare module 'azdata' {
|
||||
export function getConnection(uri: string): Thenable<ConnectionProfile>;
|
||||
}
|
||||
|
||||
export namespace nb {
|
||||
export interface NotebookDocument {
|
||||
/**
|
||||
* Sets the trust mode for the notebook document.
|
||||
*/
|
||||
setTrusted(state: boolean);
|
||||
}
|
||||
}
|
||||
|
||||
export type SqlDbType = 'BigInt' | 'Binary' | 'Bit' | 'Char' | 'DateTime' | 'Decimal'
|
||||
| 'Float' | 'Image' | 'Int' | 'Money' | 'NChar' | 'NText' | 'NVarChar' | 'Real'
|
||||
|
||||
@@ -362,6 +362,11 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements
|
||||
return Promise.resolve(this.doOpenEditor(resource, options));
|
||||
}
|
||||
|
||||
$trySetTrusted(uriComponent: UriComponents, isTrusted: boolean): Promise<boolean> {
|
||||
let uri = URI.revive(uriComponent);
|
||||
return this._notebookService.setTrusted(uri, isTrusted);
|
||||
}
|
||||
|
||||
$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleNotebookEditOperation[], opts: IUndoStopOptions): Promise<boolean> {
|
||||
let editor = this.getEditor(id);
|
||||
if (!editor) {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ok } from 'vs/base/common/assert';
|
||||
@@ -50,6 +49,7 @@ export class ExtHostNotebookDocumentData implements IDisposable {
|
||||
get cells() { return data._cells; },
|
||||
get kernelSpec() { return data._kernelSpec; },
|
||||
save() { return data._save(); },
|
||||
setTrusted(isTrusted) { data._setTrusted(isTrusted); },
|
||||
validateCellRange(range) { return data._validateRange(range); },
|
||||
};
|
||||
}
|
||||
@@ -61,7 +61,13 @@ export class ExtHostNotebookDocumentData implements IDisposable {
|
||||
return Promise.reject(new Error('Document has been closed'));
|
||||
}
|
||||
return this._proxy.$trySaveDocument(this._uri);
|
||||
}
|
||||
|
||||
private _setTrusted(isTrusted: boolean): Thenable<boolean> {
|
||||
if (this._isDisposed) {
|
||||
return Promise.reject(new Error('Document has been closed'));
|
||||
}
|
||||
return this._proxy.$trySetTrusted(this._uri, isTrusted);
|
||||
}
|
||||
|
||||
public onModelChanged(data: INotebookModelChangedData) {
|
||||
|
||||
@@ -82,6 +82,10 @@ export class NotebookEditorEdit {
|
||||
return range;
|
||||
}
|
||||
|
||||
setTrusted(isTrusted: boolean) {
|
||||
this._document.setTrusted(isTrusted);
|
||||
}
|
||||
|
||||
insertCell(value: Partial<azdata.nb.ICellContents>, index?: number, collapsed?: boolean): void {
|
||||
if (index === null || index === undefined) {
|
||||
// If not specified, assume adding to end of list
|
||||
|
||||
@@ -891,6 +891,7 @@ export interface ExtHostNotebookDocumentsAndEditorsShape {
|
||||
}
|
||||
|
||||
export interface MainThreadNotebookDocumentsAndEditorsShape extends IDisposable {
|
||||
$trySetTrusted(_uri: UriComponents, isTrusted: boolean): Thenable<boolean>;
|
||||
$trySaveDocument(uri: UriComponents): Thenable<boolean>;
|
||||
$tryShowNotebookDocument(resource: UriComponents, options: INotebookShowOptions): Promise<string>;
|
||||
$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleNotebookEditOperation[], opts: IUndoStopOptions): Promise<boolean>;
|
||||
|
||||
@@ -58,7 +58,7 @@ export class LinkHandlerDirective {
|
||||
// ignore
|
||||
}
|
||||
if (uri && this.openerService && this.isSupportedLink(uri)) {
|
||||
if (uri.fragment && uri.fragment.length > 0 && uri.fsPath === this.workbenchFilePath.fsPath) {
|
||||
if (uri.fragment && uri.fragment.length > 0 && uri.path === this.workbenchFilePath.path) {
|
||||
this.notebookService.navigateTo(this.notebookUri, uri.fragment);
|
||||
} else {
|
||||
this.openerService.open(uri).catch(onUnexpectedError);
|
||||
|
||||
@@ -205,6 +205,9 @@ export class NotebookServiceStub implements INotebookService {
|
||||
get languageMagics(): ILanguageMagic[] {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
setTrusted(notebookUri: URI, isTrusted: boolean): Promise<boolean> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
registerProvider(providerId: string, provider: INotebookProvider): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
@@ -233,6 +233,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
|
||||
public set trustedMode(isTrusted: boolean) {
|
||||
this._trustedMode = isTrusted;
|
||||
|
||||
if (this._cells) {
|
||||
this._cells.forEach(c => {
|
||||
c.trustedMode = this._trustedMode;
|
||||
@@ -290,6 +291,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
public async loadContents(isTrusted: boolean = false): Promise<void> {
|
||||
try {
|
||||
this._trustedMode = isTrusted;
|
||||
|
||||
let contents = null;
|
||||
|
||||
if (this._notebookOptions && this._notebookOptions.contentManager) {
|
||||
|
||||
@@ -112,6 +112,13 @@ export interface INotebookService {
|
||||
* @param sectionId ID of the section to navigate to
|
||||
*/
|
||||
navigateTo(notebookUri: URI, sectionId: string): void;
|
||||
|
||||
/**
|
||||
* Sets the trusted mode for the sepcified notebook.
|
||||
* @param notebookUri URI of the notebook to navigate to
|
||||
* @param isTrusted True if notebook is to be set to trusted, false otherwise.
|
||||
*/
|
||||
setTrusted(notebookUri: URI, isTrusted: boolean): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface INotebookProvider {
|
||||
|
||||
@@ -606,4 +606,25 @@ export class NotebookService extends Disposable implements INotebookService {
|
||||
editor.navigateToSection(sectionId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trusts a notebook with the specified URI.
|
||||
* @param notebookUri The notebook URI to set the trusted mode for.
|
||||
* @param isTrusted True if the notebook is to be trusted, false otherwise.
|
||||
*/
|
||||
async setTrusted(notebookUri: URI, isTrusted: boolean): Promise<boolean> {
|
||||
let editor = this.findNotebookEditor(notebookUri);
|
||||
|
||||
if (editor && editor.model) {
|
||||
if (isTrusted) {
|
||||
this._trustedCacheQueue.push(notebookUri);
|
||||
} else {
|
||||
this._unTrustedCacheQueue.push(notebookUri);
|
||||
}
|
||||
await this.updateTrustedCache();
|
||||
editor.model.trustedMode = isTrusted;
|
||||
}
|
||||
|
||||
return isTrusted;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user