diff --git a/client/build/components/AssetPreview/index.js b/client/build/components/AssetPreview/index.js index c17b70e3..994b75ce 100644 --- a/client/build/components/AssetPreview/index.js +++ b/client/build/components/AssetPreview/index.js @@ -19,7 +19,7 @@ var AssetPreview = function AssetPreview(_ref) { fileExt = _ref$claimData.fileExt, contentType = _ref$claimData.contentType, thumbnail = _ref$claimData.thumbnail; - var directSourceLink = "".concat(claimId, "/").concat(name, ".").concat(fileExt); + var directSourceLink = "asset/".concat(name, "/").concat(claimId); var showUrlLink = "/".concat(claimId, "/").concat(name); return _react.default.createElement(_reactRouterDom.Link, { to: showUrlLink diff --git a/client/build/components/AssetShareButtons/index.js b/client/build/components/AssetShareButtons/index.js new file mode 100644 index 00000000..8b54ec2b --- /dev/null +++ b/client/build/components/AssetShareButtons/index.js @@ -0,0 +1,38 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _react = _interopRequireDefault(require("react")); + +var _SpaceBetween = _interopRequireDefault(require("@components/SpaceBetween")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var AssetShareButtons = function AssetShareButtons(_ref) { + var host = _ref.host, + name = _ref.name, + shortId = _ref.shortId; + return _react.default.createElement(_SpaceBetween.default, null, _react.default.createElement("a", { + className: "link--primary", + target: "_blank", + href: "https://twitter.com/intent/tweet?text=".concat(host, "/").concat(shortId, "/").concat(name) + }, "twitter"), _react.default.createElement("a", { + className: "link--primary", + target: "_blank", + href: "https://www.facebook.com/sharer/sharer.php?u=".concat(host, "/").concat(shortId, "/").concat(name) + }, "facebook"), _react.default.createElement("a", { + className: "link--primary", + target: "_blank", + href: "http://tumblr.com/widgets/share/tool?canonicalUrl=".concat(host, "/").concat(shortId, "/").concat(name) + }, "tumblr"), _react.default.createElement("a", { + className: "link--primary", + target: "_blank", + href: "https://www.reddit.com/submit?url=".concat(host, "/").concat(shortId, "/").concat(name, "&title=").concat(name) + }, "reddit")); +}; + +var _default = AssetShareButtons; +exports.default = _default; \ No newline at end of file diff --git a/client/build/components/ClickToCopy/index.js b/client/build/components/ClickToCopy/index.js new file mode 100644 index 00000000..0c0b93e5 --- /dev/null +++ b/client/build/components/ClickToCopy/index.js @@ -0,0 +1,82 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _react = _interopRequireDefault(require("react")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } _setPrototypeOf(subClass.prototype, superClass && superClass.prototype); if (superClass) _setPrototypeOf(subClass, superClass); } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } + +function _getPrototypeOf(o) { _getPrototypeOf = Object.getPrototypeOf || function _getPrototypeOf(o) { return o.__proto__; }; return _getPrototypeOf(o); } + +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } + +var ClickToCopy = +/*#__PURE__*/ +function (_React$Component) { + function ClickToCopy(props) { + var _this; + + _classCallCheck(this, ClickToCopy); + + _this = _possibleConstructorReturn(this, _getPrototypeOf(ClickToCopy).call(this, props)); + _this.copyToClipboard = _this.copyToClipboard.bind(_assertThisInitialized(_assertThisInitialized(_this))); + return _this; + } + + _createClass(ClickToCopy, [{ + key: "copyToClipboard", + value: function copyToClipboard(event) { + var elementToCopy = event.target.id; + var element = document.getElementById(elementToCopy); + element.select(); + + try { + document.execCommand('copy'); + } catch (err) { + this.setState({ + error: 'Oops, unable to copy' + }); + } + } + }, { + key: "render", + value: function render() { + var _this$props = this.props, + id = _this$props.id, + value = _this$props.value; + return _react.default.createElement("input", { + id: id, + value: value, + onClick: this.copyToClipboard, + type: "text", + className: "click-to-copy", + readOnly: true, + spellCheck: "false" + }); + } + }]); + + _inherits(ClickToCopy, _React$Component); + + return ClickToCopy; +}(_react.default.Component); + +var _default = ClickToCopy; +exports.default = _default; \ No newline at end of file diff --git a/client/build/containers/AssetDisplay/view.js b/client/build/containers/AssetDisplay/view.js index f034edf7..c0d1f013 100644 --- a/client/build/containers/AssetDisplay/view.js +++ b/client/build/containers/AssetDisplay/view.js @@ -82,7 +82,7 @@ function (_React$Component) { case 'image/gif': return _react.default.createElement("img", { className: "asset-image", - src: "/".concat(claimId, "/").concat(name, ".").concat(fileExt), + src: "/asset/".concat(name, "/").concat(claimId), alt: name }); @@ -92,7 +92,7 @@ function (_React$Component) { controls: true, poster: thumbnail }, _react.default.createElement("source", { - src: "/".concat(claimId, "/").concat(name, ".").concat(fileExt) + src: "/asset/".concat(name, "/").concat(claimId) }), _react.default.createElement("p", null, "Your browser does not support the ", _react.default.createElement("code", null, "video"), " element.")); default: diff --git a/client/build/containers/AssetInfo/view.js b/client/build/containers/AssetInfo/view.js index a405af12..55eefee1 100644 --- a/client/build/containers/AssetInfo/view.js +++ b/client/build/containers/AssetInfo/view.js @@ -17,6 +17,10 @@ var _Row = _interopRequireDefault(require("@components/Row")); var _SpaceBetween = _interopRequireDefault(require("@components/SpaceBetween")); +var _AssetShareButtons = _interopRequireDefault(require("@components/AssetShareButtons")); + +var _ClickToCopy = _interopRequireDefault(require("@components/ClickToCopy")); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } @@ -33,80 +37,20 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } -function _getPrototypeOf(o) { _getPrototypeOf = Object.getPrototypeOf || function _getPrototypeOf(o) { return o.__proto__; }; return _getPrototypeOf(o); } - function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } -var AssetShareButtons = function AssetShareButtons(_ref) { - var host = _ref.host, - name = _ref.name, - shortId = _ref.shortId; - return _react.default.createElement(_SpaceBetween.default, null, _react.default.createElement("a", { - className: "link--primary", - target: "_blank", - href: "https://twitter.com/intent/tweet?text=".concat(host, "/").concat(shortId, "/").concat(name) - }, "twitter"), _react.default.createElement("a", { - className: "link--primary", - target: "_blank", - href: "https://www.facebook.com/sharer/sharer.php?u=".concat(host, "/").concat(shortId, "/").concat(name) - }, "facebook"), _react.default.createElement("a", { - className: "link--primary", - target: "_blank", - href: "http://tumblr.com/widgets/share/tool?canonicalUrl=".concat(host, "/").concat(shortId, "/").concat(name) - }, "tumblr"), _react.default.createElement("a", { - className: "link--primary", - target: "_blank", - href: "https://www.reddit.com/submit?url=".concat(host, "/").concat(shortId, "/").concat(name, "&title=").concat(name) - }, "reddit")); -}; - -var ClickToCopy = function ClickToCopy(_ref2) { - var id = _ref2.id, - value = _ref2.value, - copyToClipboard = _ref2.copyToClipboard; - return _react.default.createElement("input", { - id: id, - value: value, - onClick: copyToClipboard, - type: "text", - className: "click-to-copy", - readOnly: true, - spellCheck: "false" - }); -}; +function _getPrototypeOf(o) { _getPrototypeOf = Object.getPrototypeOf || function _getPrototypeOf(o) { return o.__proto__; }; return _getPrototypeOf(o); } var AssetInfo = /*#__PURE__*/ function (_React$Component) { - function AssetInfo(props) { - var _this; - + function AssetInfo() { _classCallCheck(this, AssetInfo); - _this = _possibleConstructorReturn(this, _getPrototypeOf(AssetInfo).call(this, props)); - _this.copyToClipboard = _this.copyToClipboard.bind(_assertThisInitialized(_assertThisInitialized(_this))); - return _this; + return _possibleConstructorReturn(this, _getPrototypeOf(AssetInfo).apply(this, arguments)); } _createClass(AssetInfo, [{ - key: "copyToClipboard", - value: function copyToClipboard(event) { - console.log('event:', event); - console.log('event.target:', event.target); - console.log('event.target.id:', event.target.id); - var elementToCopy = event.target.id; - var element = document.getElementById(elementToCopy); - element.select(); - - try { - document.execCommand('copy'); - } catch (err) { - this.setState({ - error: 'Oops, unable to copy' - }); - } - } - }, { key: "render", value: function render() { var _this$props$asset = this.props.asset, @@ -134,31 +78,29 @@ function (_React$Component) { label: _react.default.createElement(_Label.default, { value: 'Share:' }), - content: _react.default.createElement(AssetShareButtons, { + content: _react.default.createElement(_AssetShareButtons.default, { host: host, + name: name, shortId: shortId }) })), _react.default.createElement(_Row.default, null, _react.default.createElement(_RowLabeled.default, { label: _react.default.createElement(_Label.default, { value: 'Link:' }), - content: _react.default.createElement(ClickToCopy, { + content: _react.default.createElement(_ClickToCopy.default, { id: 'short-link', - value: "".concat(host, "/").concat(shortId, "/").concat(name, ".").concat(fileExt), - copyToClipboard: this.copyToClipboard + value: "".concat(host, "/").concat(shortId, "/").concat(name, ".").concat(fileExt) }) })), _react.default.createElement(_Row.default, null, _react.default.createElement(_RowLabeled.default, { label: _react.default.createElement(_Label.default, { value: 'Embed:' }), - content: _react.default.createElement("div", null, contentType === 'video/mp4' ? _react.default.createElement(ClickToCopy, { + content: _react.default.createElement("div", null, contentType === 'video/mp4' ? _react.default.createElement(_ClickToCopy.default, { id: 'embed-text-video', - value: ""), - copyToClipboard: this.copyToClipboard - }) : _react.default.createElement(ClickToCopy, { + value: "") + }) : _react.default.createElement(_ClickToCopy.default, { id: 'embed-text-image', - value: ""), - copyToClipboard: this.copyToClipboard + value: "") })) })), _react.default.createElement(_Row.default, null, _react.default.createElement(_SpaceBetween.default, null, _react.default.createElement(_reactRouterDom.Link, { className: "link--primary", diff --git a/client/scss/components/_asset-display.scss b/client/scss/components/_asset-display.scss index 05b76c92..6950815b 100644 --- a/client/scss/components/_asset-display.scss +++ b/client/scss/components/_asset-display.scss @@ -2,7 +2,8 @@ flex: 1 0 auto; display: flex; justify-content: center; - align-items: center; + /*align-items: center;*/ + flex-direction: column; } .asset-image, .asset-video { diff --git a/client/src/components/AssetPreview/index.jsx b/client/src/components/AssetPreview/index.jsx index ae721a08..e9e10cef 100644 --- a/client/src/components/AssetPreview/index.jsx +++ b/client/src/components/AssetPreview/index.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; const AssetPreview = ({ defaultThumbnail, claimData: { name, claimId, fileExt, contentType, thumbnail } }) => { - const directSourceLink = `${claimId}/${name}.${fileExt}`; + const directSourceLink = `asset/${name}/${claimId}`; const showUrlLink = `/${claimId}/${name}`; return ( diff --git a/client/src/components/AssetShareButtons/index.js b/client/src/components/AssetShareButtons/index.js new file mode 100644 index 00000000..dfb0c0b9 --- /dev/null +++ b/client/src/components/AssetShareButtons/index.js @@ -0,0 +1,39 @@ +import React from 'react'; +import SpaceBetween from '@components/SpaceBetween'; + +const AssetShareButtons = ({ host, name, shortId }) => { + return ( + + + twitter + + + facebook + + + tumblr + + + reddit + + + ); +}; + +export default AssetShareButtons; diff --git a/client/src/components/ClickToCopy/index.jsx b/client/src/components/ClickToCopy/index.jsx new file mode 100644 index 00000000..35b7dba8 --- /dev/null +++ b/client/src/components/ClickToCopy/index.jsx @@ -0,0 +1,34 @@ +import React from 'react'; + +class ClickToCopy extends React.Component { + constructor (props) { + super(props); + this.copyToClipboard = this.copyToClipboard.bind(this); + } + copyToClipboard (event) { + const elementToCopy = event.target.id; + const element = document.getElementById(elementToCopy); + element.select(); + try { + document.execCommand('copy'); + } catch (err) { + this.setState({error: 'Oops, unable to copy'}); + } + } + render () { + const {id, value} = this.props; + return ( + + ); + } +} + +export default ClickToCopy; diff --git a/client/src/containers/AssetDisplay/view.jsx b/client/src/containers/AssetDisplay/view.jsx index 277a4980..c742c851 100644 --- a/client/src/containers/AssetDisplay/view.jsx +++ b/client/src/containers/AssetDisplay/view.jsx @@ -39,7 +39,7 @@ class AssetDisplay extends React.Component { return ( {name} ); @@ -49,7 +49,9 @@ class AssetDisplay extends React.Component { className='asset-video' controls poster={thumbnail} > - +

Your browser does not support the video element.

); diff --git a/client/src/containers/AssetInfo/view.jsx b/client/src/containers/AssetInfo/view.jsx index 12427c27..e251e81d 100644 --- a/client/src/containers/AssetInfo/view.jsx +++ b/client/src/containers/AssetInfo/view.jsx @@ -4,74 +4,10 @@ import Label from '@components/Label'; import RowLabeled from '@components/RowLabeled'; import Row from '@components/Row'; import SpaceBetween from '@components/SpaceBetween'; - -const AssetShareButtons = ({ host, name, shortId }) => { - return ( - - - twitter - - - facebook - - - tumblr - - - reddit - - - ); -}; - -const ClickToCopy = ({id, value, copyToClipboard}) => { - return ( - - ); -}; +import AssetShareButtons from '@components/AssetShareButtons'; +import ClickToCopy from '@components/ClickToCopy'; class AssetInfo extends React.Component { - constructor (props) { - super(props); - this.copyToClipboard = this.copyToClipboard.bind(this); - } - copyToClipboard (event) { - console.log('event:', event); - console.log('event.target:', event.target); - console.log('event.target.id:', event.target.id); - const elementToCopy = event.target.id; - const element = document.getElementById(elementToCopy); - element.select(); - try { - document.execCommand('copy'); - } catch (err) { - this.setState({error: 'Oops, unable to copy'}); - } - } render () { const { asset: { shortId, claimData : { channelName, certificateId, description, name, claimId, fileExt, contentType, thumbnail, host } } } = this.props; return ( @@ -99,6 +35,7 @@ class AssetInfo extends React.Component { content={ } @@ -114,7 +51,6 @@ class AssetInfo extends React.Component { } /> @@ -131,13 +67,11 @@ class AssetInfo extends React.Component { `} - copyToClipboard={this.copyToClipboard} /> ) : ( `} - copyToClipboard={this.copyToClipboard} /> )} diff --git a/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js b/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js index d7b509e9..66f57be9 100644 --- a/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js +++ b/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js @@ -12,9 +12,18 @@ const parsePublishApiRequestFiles = ({file, thumbnail}) => { throw new Error('no file type found'); } if (!file.size) { - throw new Error('no file type found'); + throw new Error('no file size found'); } // validate the file name + if (!file.name) { + throw new Error('no file name found'); + } + if (file.name.indexOf('.') < 0) { + throw new Error('no file extension found in file name'); + } + if (file.name.indexOf('.') === 0) { + throw new Error('file name cannot start with "."'); + } if (/'/.test(file.name)) { throw new Error('apostrophes are not allowed in the file name'); } diff --git a/server/controllers/api/claim/publish/validateFileTypeAndSize.js b/server/controllers/api/claim/publish/validateFileTypeAndSize.js index ca3d4f17..7f9ddef4 100644 --- a/server/controllers/api/claim/publish/validateFileTypeAndSize.js +++ b/server/controllers/api/claim/publish/validateFileTypeAndSize.js @@ -25,7 +25,7 @@ const validateFileTypeAndSize = (file) => { break; default: logger.debug('publish > file validation > unrecognized file type'); - throw new Error('The ' + file.type + ' content type is not supported. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.'); + throw new Error('The ' + file.type + ' content type is not supported. Only, image/jpg, image/png, image/gif, and video/mp4 content types are currently supported.'); } return file; }; diff --git a/server/controllers/assets/utils/serveFile.js b/server/controllers/assets/utils/serveFile.js index ab3c167a..7344fdb2 100644 --- a/server/controllers/assets/utils/serveFile.js +++ b/server/controllers/assets/utils/serveFile.js @@ -1,13 +1,16 @@ const logger = require('winston'); const serveFile = ({ filePath, fileType }, res) => { - logger.verbose(`serving file: ${filePath}`); + if (!fileType) { + logger.error(`no fileType provided for ${filePath}`); + } const sendFileOptions = { headers: { 'X-Content-Type-Options': 'nosniff', - 'Content-Type' : fileType || 'image/jpeg', + 'Content-Type' : fileType, }, }; + logger.debug(`fileOptions for ${filePath}:`, sendFileOptions); res.status(200).sendFile(filePath, sendFileOptions); };