Skip to content

Commit

Permalink
Backport PR jupyterlab#7022: Fix Vega downloads and download URLs in …
Browse files Browse the repository at this point in the history
…general
  • Loading branch information
blink1073 authored and MeeseeksDev[bot] committed Aug 16, 2019
1 parent f01e381 commit efea79d
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 18 deletions.
3 changes: 3 additions & 0 deletions packages/attachments/src/model.ts
Expand Up @@ -374,6 +374,9 @@ export class AttachmentsResolver implements IRenderMime.IResolver {

/**
* Get the download url of a given absolute server path.
*
* #### Notes
* The returned URL may include a query parameter.
*/
getDownloadUrl(path: string): Promise<string> {
if (this._parent && !path.startsWith('attachment:')) {
Expand Down
7 changes: 2 additions & 5 deletions packages/filebrowser/src/model.ts
Expand Up @@ -315,12 +315,9 @@ export class FileBrowserModel implements IDisposable {
async download(path: string): Promise<void> {
const url = await this.manager.services.contents.getDownloadUrl(path);
let element = document.createElement('a');
element.href = url;
element.download = '';
document.body.appendChild(element);
element.setAttribute('href', url);
// Chrome doesn't get the right name automatically
const parts = path.split('/');
const name = parts[parts.length - 1];
element.setAttribute('download', name);
element.click();
document.body.removeChild(element);
return void 0;
Expand Down
3 changes: 3 additions & 0 deletions packages/rendermime-interfaces/src/index.ts
Expand Up @@ -340,6 +340,9 @@ export namespace IRenderMime {

/**
* Get the download url for a given absolute url path.
*
* #### Notes
* This URL may include a query parameter.
*/
getDownloadUrl(url: string): Promise<string>;

Expand Down
3 changes: 3 additions & 0 deletions packages/rendermime/src/registry.ts
Expand Up @@ -328,6 +328,9 @@ export namespace RenderMimeRegistry {

/**
* Get the download url of a given absolute url path.
*
* #### Notes
* This URL may include a query parameter.
*/
getDownloadUrl(url: string): Promise<string> {
if (this.isLocal(url)) {
Expand Down
14 changes: 13 additions & 1 deletion packages/services/src/contents/index.ts
Expand Up @@ -276,6 +276,9 @@ export namespace Contents {
*
* @param A promise which resolves with the absolute POSIX
* file path on the server.
*
* #### Notes
* The returned URL may include a query parameter.
*/
getDownloadUrl(path: string): Promise<string>;

Expand Down Expand Up @@ -420,6 +423,9 @@ export namespace Contents {
*
* @param A promise which resolves with the absolute POSIX
* file path on the server.
*
* #### Notes
* The returned URL may include a query parameter.
*/
getDownloadUrl(localPath: string): Promise<string>;

Expand Down Expand Up @@ -690,6 +696,8 @@ export class ContentsManager implements Contents.IManager {
*
* #### Notes
* It is expected that the path contains no relative paths.
*
* The returned URL may include a query parameter.
*/
getDownloadUrl(path: string): Promise<string> {
let [drive, localPath] = this._driveForPath(path);
Expand Down Expand Up @@ -1041,13 +1049,17 @@ export class Drive implements Contents.IDrive {
*
* #### Notes
* It is expected that the path contains no relative paths.
*
* The returned URL may include a query parameter.
*/
getDownloadUrl(localPath: string): Promise<string> {
let baseUrl = this.serverSettings.baseUrl;
let url = URLExt.join(baseUrl, FILES_URL, URLExt.encodeParts(localPath));
const xsrfTokenMatch = document.cookie.match('\\b_xsrf=([^;]*)\\b');
if (xsrfTokenMatch) {
url = URLExt.join(url, `?_xsrf=${xsrfTokenMatch[1]}`);
const fullurl = new URL(url);
fullurl.searchParams.append('_xsrf', xsrfTokenMatch[1]);
url = fullurl.toString();
}
return Promise.resolve(url);
}
Expand Down
24 changes: 18 additions & 6 deletions packages/vega4-extension/src/index.ts
Expand Up @@ -42,6 +42,11 @@ export const VEGA_MIME_TYPE = 'application/vnd.vega.v4+json';
*/
export const VEGALITE_MIME_TYPE = 'application/vnd.vegalite.v2+json';

/**
* A regex to test for a protocol in a URI.
*/
const protocolRegex = /^[A-Za-z]:/;

/**
* A widget for rendering Vega or Vega-Lite data, for usage with rendermime.
*/
Expand Down Expand Up @@ -76,8 +81,6 @@ export class RenderedVega extends Widget implements IRenderMime.IRenderer {

const vega =
Private.vega != null ? Private.vega : await Private.ensureVega();
const path = await this._resolver.resolveUrl('');
const baseURL = await this._resolver.getDownloadUrl(path);

const el = document.createElement('div');

Expand All @@ -89,15 +92,24 @@ export class RenderedVega extends Widget implements IRenderMime.IRenderer {
this._result.view.finalize();
}

const loader = vega.vega.loader({
http: { credentials: 'same-origin' }
});

const sanitize = async (uri: string, options: any) => {
// If the uri is a path, get the download URI
if (!protocolRegex.test(uri)) {
uri = await this._resolver.getDownloadUrl(uri);
}
return loader.sanitize(uri, options);
};

this._result = await vega.default(el, spec, {
actions: true,
defaultStyle: true,
...embedOptions,
mode,
loader: {
baseURL,
http: { credentials: 'same-origin' }
}
loader: { ...loader, sanitize }
});

if (model.data['image/png']) {
Expand Down
23 changes: 17 additions & 6 deletions packages/vega5-extension/src/index.ts
Expand Up @@ -42,6 +42,11 @@ export const VEGA_MIME_TYPE = 'application/vnd.vega.v5+json';
*/
export const VEGALITE_MIME_TYPE = 'application/vnd.vegalite.v3+json';

/**
* A regex to test for a protocol in a URI.
*/
const protocolRegex = /^[A-Za-z]:/;

/**
* A widget for rendering Vega or Vega-Lite data, for usage with rendermime.
*/
Expand Down Expand Up @@ -76,8 +81,6 @@ export class RenderedVega extends Widget implements IRenderMime.IRenderer {

const vega =
Private.vega != null ? Private.vega : await Private.ensureVega();
const path = await this._resolver.resolveUrl('');
const baseURL = await this._resolver.getDownloadUrl(path);

const el = document.createElement('div');

Expand All @@ -89,15 +92,23 @@ export class RenderedVega extends Widget implements IRenderMime.IRenderer {
this._result.view.finalize();
}

const loader = vega.vega.loader({
http: { credentials: 'same-origin' }
});
const sanitize = async (uri: string, options: any) => {
// If the uri is a path, get the download URI
if (!protocolRegex.test(uri)) {
uri = await this._resolver.getDownloadUrl(uri);
}
return loader.sanitize(uri, options);
};

this._result = await vega.default(el, spec, {
actions: true,
defaultStyle: true,
...embedOptions,
mode,
loader: {
baseURL,
http: { credentials: 'same-origin' }
}
loader: { ...loader, sanitize }
});

if (model.data['image/png']) {
Expand Down

0 comments on commit efea79d

Please sign in to comment.