diff --git a/packages/netlify-cms-backend-github/src/API.js b/packages/netlify-cms-backend-github/src/API.js index 9815ad33..bf4e6bb7 100644 --- a/packages/netlify-cms-backend-github/src/API.js +++ b/packages/netlify-cms-backend-github/src/API.js @@ -102,14 +102,14 @@ export default class API { return textPromise; } - request(path, options = {}) { + request(path, options = {}, parseResponse = response => this.parseResponse(response)) { const headers = this.requestHeaders(options.headers || {}); const url = this.urlFor(path, options); let responseStatus; return fetch(url, { ...options, headers }) .then(response => { responseStatus = response.status; - return this.parseResponse(response); + return parseResponse(response); }) .catch(error => { throw new APIError(error.message, responseStatus, 'GitHub'); diff --git a/packages/netlify-cms-backend-github/src/GraphQLAPI.js b/packages/netlify-cms-backend-github/src/GraphQLAPI.js index d324f7d9..aa34ad87 100644 --- a/packages/netlify-cms-backend-github/src/GraphQLAPI.js +++ b/packages/netlify-cms-backend-github/src/GraphQLAPI.js @@ -170,7 +170,7 @@ export default class GraphQLAPI extends API { } else if (!is_binary) { return text; } else { - return super.retrieveBlob(sha); + return super.retrieveBlob(sha, repoURL); } } @@ -194,12 +194,13 @@ export default class GraphQLAPI extends API { }); if (data.repository.object) { - const files = data.repository.object.entries.map(e => ({ - ...e, - path: `${path}/${e.name}`, - download_url: `https://raw.githubusercontent.com/${this.repo}/${this.branch}/${path}/${e.name}`, - size: e.blob && e.blob.size, - })); + const files = data.repository.object.entries + .filter(({ type }) => type === 'blob') + .map(e => ({ + ...e, + path: `${path}/${e.name}`, + size: e.blob && e.blob.size, + })); return files; } else { throw new APIError('Not Found', 404, 'GitHub'); @@ -589,15 +590,15 @@ export default class GraphQLAPI extends API { let entries = null; if (commitTree.data.repository.commit.tree) { - entries = commitTree.data.repository.commit.tree.entries; + ({ entries, sha } = commitTree.data.repository.commit.tree); } if (tree.data.repository.tree.entries) { - entries = tree.data.repository.tree.entries; + ({ entries, sha } = tree.data.repository.tree); } if (entries) { - return { tree: entries.map(e => ({ ...e, mode: TREE_ENTRY_TYPE_TO_MODE[e.type] })) }; + return { sha, tree: entries.map(e => ({ ...e, mode: TREE_ENTRY_TYPE_TO_MODE[e.type] })) }; } return Promise.reject('Could not get tree'); diff --git a/packages/netlify-cms-backend-github/src/fragments.js b/packages/netlify-cms-backend-github/src/fragments.js index 042ad745..eb61dd86 100644 --- a/packages/netlify-cms-backend-github/src/fragments.js +++ b/packages/netlify-cms-backend-github/src/fragments.js @@ -67,6 +67,7 @@ export const fileEntry = gql` fragment FileEntryParts on TreeEntry { name sha: oid + type blob: object { ... on Blob { size: byteSize diff --git a/packages/netlify-cms-backend-github/src/implementation.js b/packages/netlify-cms-backend-github/src/implementation.js index 0f7c1602..c8ef661f 100644 --- a/packages/netlify-cms-backend-github/src/implementation.js +++ b/packages/netlify-cms-backend-github/src/implementation.js @@ -259,15 +259,34 @@ export default class GitHub { getMedia() { return this.api.listFiles(this.config.get('media_folder')).then(files => files.map(({ sha, name, size, download_url, path }) => { - const url = new URL(download_url); - if (url.pathname.match(/.svg$/)) { - url.search += (url.search.slice(1) === '' ? '?' : '&') + 'sanitize=true'; + if (download_url) { + const url = new URL(download_url); + if (url.pathname.match(/.svg$/)) { + url.search += (url.search.slice(1) === '' ? '?' : '&') + 'sanitize=true'; + } + // if 'displayURL' is a string it will be loaded as is + return { id: sha, name, size, displayURL: url.href, path }; + } else { + // if 'displayURL' is not a string it will be loaded using getMediaDisplayURL + return { id: sha, name, size, displayURL: { sha }, path }; } - return { id: sha, name, size, displayURL: url.href, path }; }), ); } + async getMediaDisplayURL(displayURL) { + const { sha } = displayURL; + const blob = await this.api.request( + `${this.api.repoURL}/git/blobs/${sha}`, + { + headers: { Accept: 'application/vnd.github.VERSION.raw' }, + }, + response => response.blob(), + ); + + return URL.createObjectURL(blob); + } + persistEntry(entry, mediaFiles = [], options = {}) { return this.api.persistFiles(entry, mediaFiles, options); } diff --git a/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCard.js b/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCard.js index d9b5bb27..ee5486c8 100644 --- a/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCard.js +++ b/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCard.js @@ -100,6 +100,8 @@ MediaLibraryCard.propTypes = { margin: PropTypes.string.isRequired, isPrivate: PropTypes.bool, type: PropTypes.string, + isViewableImage: PropTypes.bool.isRequired, + loadDisplayURL: PropTypes.func.isRequired, }; export default MediaLibraryCard; diff --git a/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCardGrid.js b/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCardGrid.js index 404e9207..68a6bf44 100644 --- a/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCardGrid.js +++ b/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCardGrid.js @@ -86,6 +86,7 @@ MediaLibraryCardGrid.propTypes = { cardMargin: PropTypes.string.isRequired, loadDisplayURL: PropTypes.func.isRequired, isPrivate: PropTypes.bool, + displayURLs: PropTypes.instanceOf(Map).isRequired, }; export default MediaLibraryCardGrid; diff --git a/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryModal.js b/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryModal.js index 99a53d97..29f6c95e 100644 --- a/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryModal.js +++ b/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryModal.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import styled from '@emotion/styled'; +import { Map } from 'immutable'; import { isEmpty } from 'lodash'; import { translate } from 'react-polyglot'; import { Modal } from 'UI'; @@ -219,6 +220,7 @@ MediaLibraryModal.propTypes = { handleLoadMore: PropTypes.func.isRequired, loadDisplayURL: PropTypes.func.isRequired, t: PropTypes.func.isRequired, + displayURLs: PropTypes.instanceOf(Map).isRequired, }; export default translate()(MediaLibraryModal);