/
index.ts
165 lines (142 loc) · 4.24 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
import {
ILayoutRestorer,
JupyterLab,
JupyterLabPlugin
} from '@jupyterlab/application';
import { Dialog, showDialog } from '@jupyterlab/apputils';
import { ISettingRegistry } from '@jupyterlab/coreutils';
import { IDocumentManager } from '@jupyterlab/docmanager';
import { IFileBrowserFactory } from '@jupyterlab/filebrowser';
import { GitHubDrive, DEFAULT_GITHUB_BASE_URL } from './contents';
import { GitHubFileBrowser } from './browser';
import '../style/index.css';
/**
* GitHub filebrowser plugin state namespace.
*/
const NAMESPACE = 'github-filebrowser';
/**
* The ID for the plugin.
*/
const PLUGIN_ID = '@jupyterlab/github:drive';
/**
* The JupyterLab plugin for the GitHub Filebrowser.
*/
const fileBrowserPlugin: JupyterLabPlugin<void> = {
id: PLUGIN_ID,
requires: [
IDocumentManager,
IFileBrowserFactory,
ILayoutRestorer,
ISettingRegistry
],
activate: activateFileBrowser,
autoStart: true
};
/**
* Activate the file browser.
*/
function activateFileBrowser(
app: JupyterLab,
manager: IDocumentManager,
factory: IFileBrowserFactory,
restorer: ILayoutRestorer,
settingRegistry: ISettingRegistry
): void {
const { commands } = app;
// Add the GitHub backend to the contents manager.
const drive = new GitHubDrive(app.docRegistry);
manager.services.contents.addDrive(drive);
// Create the embedded filebrowser. GitHub repos likely
// don't need as often of a refresh interval as normal ones,
// and rate-limiting can be an issue, so we give a 5 minute
// refresh interval.
const browser = factory.createFileBrowser(NAMESPACE, {
commands,
driveName: drive.name,
refreshInterval: 300000
});
const gitHubBrowser = new GitHubFileBrowser(browser, drive);
gitHubBrowser.title.iconClass = 'jp-GitHub-icon jp-SideBar-tabIcon';
gitHubBrowser.title.caption = 'Browse GitHub';
gitHubBrowser.id = 'github-file-browser';
// Add the file browser widget to the application restorer.
restorer.add(gitHubBrowser, NAMESPACE);
app.shell.addToLeftArea(gitHubBrowser, { rank: 102 });
let shouldWarn = false;
const onSettingsUpdated = async (settings: ISettingRegistry.ISettings) => {
const baseUrl = settings.get('baseUrl').composite as
| string
| null
| undefined;
const accessToken = settings.get('accessToken').composite as
| string
| null
| undefined;
drive.baseUrl = baseUrl || DEFAULT_GITHUB_BASE_URL;
if (accessToken) {
let proceed = true;
if (shouldWarn) {
proceed = await Private.showWarning();
}
if (!proceed) {
settings.remove('accessToken');
} else {
drive.accessToken = accessToken;
}
} else {
drive.accessToken = null;
}
};
// Fetch the initial state of the settings.
Promise.all([settingRegistry.load(PLUGIN_ID), app.restored])
.then(([settings]) => {
settings.changed.connect(onSettingsUpdated);
onSettingsUpdated(settings);
// Don't warn about access token on initial page load, but do for every setting thereafter.
shouldWarn = true;
const defaultRepo = settings.get('defaultRepo').composite as
| string
| null;
if (defaultRepo) {
browser.model.restored.then(() => {
browser.model.cd(`/${defaultRepo}`);
});
}
})
.catch((reason: Error) => {
console.error(reason.message);
});
return;
}
export default fileBrowserPlugin;
/**
* A namespace for module-private functions.
*/
namespace Private {
/**
* Show a warning dialog about security.
*
* @returns whether the user accepted the dialog.
*/
export async function showWarning(): Promise<boolean> {
return showDialog({
title: 'Security Alert!',
body:
'Adding a client side access token can pose a security risk! ' +
'Please consider using the server extension instead.' +
'Do you want to continue?',
buttons: [
Dialog.cancelButton({ label: 'CANCEL' }),
Dialog.warnButton({ label: 'PROCEED' })
]
}).then(result => {
if (result.button.accept) {
return true;
} else {
return false;
}
});
}
}