adds licenseUrl

This commit is contained in:
jessop 2019-02-23 00:52:31 -05:00
parent a2c87af728
commit 7d5e41153e
19 changed files with 288 additions and 93 deletions

View file

@ -1,12 +1,13 @@
import React from 'react';
import RowLabeled from '@components/RowLabeled';
import Label from '@components/Label';
import { LICENSES } from '../../constants/publish_license_urls';
const PublishLicenseInput = ({ handleSelect }) => {
return (
<RowLabeled
label={
<Label value={'License:'} />
<Label value={'License'} />
}
content={
<select
@ -16,8 +17,11 @@ const PublishLicenseInput = ({ handleSelect }) => {
onChange={handleSelect}
>
<option value=''>Unspecified</option>
<option value='Public Domain'>Public Domain</option>
<option value='Creative Commons'>Creative Commons</option>
{
LICENSES.map(function(item, i){
return <option key={item + 'license key'} value={item}>{item}</option>;
})
}
</select>
}
/>

View file

@ -0,0 +1,31 @@
import React from 'react';
import RowLabeled from '@components/RowLabeled';
import Label from '@components/Label';
import { CC_LICENSES } from '../../constants/publish_license_urls';
const PublishLicenseUrlInput = ({ handleSelect }) => {
return (
<RowLabeled
label={
<Label value={'License Url'} />
}
content={
<select
type='text'
name='licenseUrl'
id='publish-license-url'
onChange={handleSelect}
>
<option value=''>Unspecified</option>
{
CC_LICENSES.map(function(item, i){
return <option key={item.url} value={item.url}>{item.value}</option>
})
}
</select>
}
/>
);
};
export default PublishLicenseUrlInput;

View file

@ -0,0 +1,33 @@
export const CC_LICENSES = [
{
value: 'CC Attr. 4.0 Int',
url: 'https://creativecommons.org/licenses/by/4.0/legalcode',
},
{
value: 'CC Attr-ShareAlike 4.0 Int',
url: 'https://creativecommons.org/licenses/by-sa/4.0/legalcode',
},
{
value: 'CC Attr-NoDerivatives 4.0 Int',
url: 'https://creativecommons.org/licenses/by-nd/4.0/legalcode',
},
{
value: 'CC Attr-NonComm 4.0 Int',
url: 'https://creativecommons.org/licenses/by-nc/4.0/legalcode',
},
{
value: 'CC Attr-NonComm-ShareAlike 4.0 Int',
url: 'https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode',
},
{
value: 'CC Attr-NonComm-NoDerivatives 4.0 Int',
url: 'https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode',
},
];
export const LICENSES = ['Public Domain', 'Other', 'Copyright', 'Creative Commons'];
export const PUBLIC_DOMAIN = 'Public Domain';
export const OTHER = 'other';
export const COPYRIGHT = 'copyright';
export const CREATIVE_COMMONS = 'Creative Commons';

View file

@ -18,7 +18,7 @@ class AssetInfo extends React.Component {
render () {
const { editable, asset } = this.props;
const { claimViews, claimData } = asset;
const { channelName, claimId, channelShortId, description, name, fileExt, contentType, host, certificateId } = claimData;
const { channelName, claimId, channelShortId, description, name, fileExt, contentType, host, certificateId, license, licenseUrl, transactionTime } = claimData;
const canonicalUrl = createCanonicalLink({ asset: { ...claimData, shortId: asset.shortId }});
const assetCanonicalUrl = `${host}${canonicalUrl}`;
@ -71,19 +71,36 @@ class AssetInfo extends React.Component {
}
/>
)}
{claimViews ? (
<RowLabeled
label={
<Label value={'Views'} />
}
content={
<span className='text'>
{claimViews}
</span>
}
/>
) : null}
<SpaceBetween>
{license && (
<RowLabeled
label={
<Label value={'License'} />
}
content={
<div className='text'>
{licenseUrl ? (
<a className={'link--primary'} href={licenseUrl} target={'_blank'}>{license}</a>
) : (
<span>{license}</span> )}
</div>
}
/>
)}
{claimViews ? (
<RowLabeled
label={
<Label value={'Views'} />
}
content={
<span className='text'>
{claimViews}
</span>
}
/>
) : null}
</SpaceBetween>
<RowLabeled
label={
<Label value={'Share'} />

View file

@ -18,10 +18,10 @@ class ChannelSelect extends React.Component {
componentWillMount () {
const { loggedInChannelName, onChannelSelect, publishOnlyApproved, onPublishInChannelChange } = this.props;
if (loggedInChannelName) {
onChannelSelect(loggedInChannelName);
this.props.onPublishInChannelChange(true);
}
if (publishOnlyApproved) {
onPublishInChannelChange(true);
this.props.onPublishInChannelChange(true);
}
}
toggleAnonymousPublish (event) {
@ -37,7 +37,10 @@ class ChannelSelect extends React.Component {
this.props.onChannelSelect(selectedOption);
}
render () {
const { publishInChannel, channelError, selectedChannel, loggedInChannelName, publishOnlyApproved } = this.props;
let { publishInChannel, channelError, selectedChannel, loggedInChannelName, publishOnlyApproved } = this.props;
if (loggedInChannelName) {
publishInChannel = 'checked';
}
if (publishOnlyApproved) {
return (
<React.Fragment>

View file

@ -1,14 +1,15 @@
import {connect} from 'react-redux';
import {updateMetadata, toggleMetadataInputs} from '../../actions/publish';
import { connect } from 'react-redux';
import { updateMetadata, toggleMetadataInputs } from '../../actions/publish';
import View from './view';
const mapStateToProps = ({ publish }) => {
return {
showMetadataInputs: publish.showMetadataInputs,
description : publish.metadata.description,
license : publish.metadata.license,
nsfw : publish.metadata.nsfw,
isUpdate : publish.isUpdate,
description: publish.metadata.description,
license: publish.metadata.license,
licenseUrl: publish.metadata.licenseUrl,
nsfw: publish.metadata.nsfw,
isUpdate: publish.isUpdate,
};
};
@ -17,10 +18,13 @@ const mapDispatchToProps = dispatch => {
onMetadataChange: (name, value) => {
dispatch(updateMetadata(name, value));
},
onToggleMetadataInputs: (value) => {
onToggleMetadataInputs: value => {
dispatch(toggleMetadataInputs(value));
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(View);
export default connect(
mapStateToProps,
mapDispatchToProps
)(View);

View file

@ -1,9 +1,9 @@
import React from 'react';
import PublishDescriptionInput from '@components/PublishDescriptionInput';
import PublishLicenseInput from '@components/PublishLicenseInput';
import PublishLicenseUrlInput from '@components/PublishLicenseUrlInput';
import PublishNsfwInput from '@components/PublishNsfwInput';
import ButtonSecondary from '@components/ButtonSecondary';
import Row from '@components/Row';
class PublishMetadataInputs extends React.Component {
constructor (props) {
@ -25,12 +25,15 @@ class PublishMetadataInputs extends React.Component {
const name = event.target.name;
const selectedOption = event.target.selectedOptions[0].value;
this.props.onMetadataChange(name, selectedOption);
if (name === 'license' && selectedOption !== 'Creative Commons'){
this.props.onMetadataChange('licenseUrl', '');
}
}
render () {
const { showMetadataInputs, description, isUpdate, nsfw } = this.props;
return (
<div>
{(showMetadataInputs || isUpdate) && (
{(showMetadataInputs || isUpdate) && (
<React.Fragment>
<PublishDescriptionInput
description={this.props.description}
@ -39,6 +42,11 @@ class PublishMetadataInputs extends React.Component {
<PublishLicenseInput
handleSelect={this.handleSelect}
/>
{ (this.props.license === 'Creative Commons') && (
<PublishLicenseUrlInput
handleSelect={this.handleSelect}
/>
)}
<PublishNsfwInput
nsfw={this.props.nsfw}
handleInput={this.handleInput}

View file

@ -9,7 +9,7 @@ class EditPage extends React.Component {
onHandleShowPageUri(match.params);
setUpdateTrue();
if (asset) {
['title', 'description', 'license', 'nsfw'].forEach(meta => updateMetadata(meta, asset.claimData[meta]));
['title', 'description', 'license', 'licenseUrl', 'nsfw'].forEach(meta => updateMetadata(meta, asset.claimData[meta]));
}
setHasChanged(false);
}

View file

@ -19,40 +19,42 @@ if (siteConfig) {
// create initial state
const initialState = {
disabled : disabledConfig,
disabledMessage : disabledMessageConfig,
publishInChannel : false,
selectedChannel : LOGIN,
disabled: disabledConfig,
disabledMessage: disabledMessageConfig,
publishInChannel: false,
selectedChannel: LOGIN,
showMetadataInputs: false,
status : {
status : null,
status: {
status: null,
message: null,
},
error: {
file : null,
url : null,
file: null,
url: null,
channel: null,
},
file : null,
claim : '',
file: null,
claim: '',
metadata: {
title : '',
title: '',
description: '',
license : '',
nsfw : false,
license: '',
licenseUrl: '',
nsfw: false,
},
isUpdate : false,
isUpdate: false,
hasChanged: false,
thumbnail : null,
thumbnail: null,
thumbnailChannel,
thumbnailChannelId,
};
export default function (state = initialState, action) {
export default function(state = initialState, action) {
switch (action.type) {
case actions.FILE_SELECTED:
return Object.assign({}, state.isUpdate ? state : initialState, { // note: clears to initial state
file : action.data,
return Object.assign({}, state.isUpdate ? state : initialState, {
// note: clears to initial state
file: action.data,
hasChanged: true,
});
case actions.FILE_CLEAR:
@ -66,13 +68,13 @@ export default function (state = initialState, action) {
});
case actions.CLAIM_UPDATE:
return Object.assign({}, state, {
claim : action.data,
claim: action.data,
hasChanged: true,
});
case actions.SET_PUBLISH_IN_CHANNEL:
return Object.assign({}, state, {
publishInChannel: action.channel,
hasChanged : true,
hasChanged: true,
});
case actions.PUBLISH_STATUS_UPDATE:
return Object.assign({}, state, {
@ -96,7 +98,7 @@ export default function (state = initialState, action) {
case actions.THUMBNAIL_NEW:
return {
...state,
thumbnail : action.data,
thumbnail: action.data,
hasChanged: true,
};
case actions.SET_UPDATE_TRUE:
@ -112,4 +114,4 @@ export default function (state = initialState, action) {
default:
return state;
}
};
}

View file

@ -1,9 +1,16 @@
export const createPublishMetadata = (claim, { type }, { title, description, license, nsfw }, publishInChannel, selectedChannel) => {
export const createPublishMetadata = (
claim,
{ type },
{ title, description, license, licenseUrl, nsfw },
publishInChannel,
selectedChannel
) => {
let metadata = {
name: claim,
title,
description,
license,
licenseUrl,
nsfw,
type,
};

View file

@ -136,6 +136,14 @@ export default (sequelize, { BOOLEAN, DATE, DECIMAL, ENUM, INTEGER, STRING, TEXT
type: STRING,
set() {},
},
license: {
type: STRING,
set() {},
},
license_url: {
type: STRING,
set() {},
},
},
{
freezeTableName: true,

View file

@ -51,7 +51,7 @@ export default (db, table, sequelize) => ({
},
getShortClaimIdFromLongClaimId: async (claimId, claimName, pendingClaim) => {
logger.debug(`claim.getShortClaimIdFromLongClaimId for ${claimName}#${claimId}`);
logger.info(`claim.getShortClaimIdFromLongClaimId for ${claimName}#${claimId}`);
return await table
.findAll({
where: { name: claimName },

View file

@ -1,8 +1,10 @@
const chainquery = require('chainquery').default;
const logger = require('winston');
const getClaimData = require('server/utils/getClaimData');
const { returnPaginatedChannelClaims } = require('./channelPagination.js');
const getChannelClaims = async (channelName, channelLongId, page) => {
logger.info(`getChannelClaims: ${channelName}, ${channelLongId}, ${page}`);
let channelShortId = await chainquery.claim.queries.getShortClaimIdFromLongClaimId(
channelLongId,
channelName

View file

@ -1,6 +1,17 @@
const logger = require('winston');
const { details, publishing } = require('@config/siteConfig');
const createPublishParams = (filePath, name, title, description, license, nsfw, thumbnail, channelName, channelClaimId) => {
const createPublishParams = (
filePath,
name,
title,
description,
license,
licenseUrl,
nsfw,
thumbnail,
channelName,
channelClaimId
) => {
// provide defaults for title
if (title === null || title.trim() === '') {
title = name;
@ -11,19 +22,24 @@ const createPublishParams = (filePath, name, title, description, license, nsfw,
}
// provide default for license
if (license === null || license.trim() === '') {
license = ''; // default to empty string
license = ''; // default to empty string
}
// provide default for licenseUrl
if (licenseUrl === null || licenseUrl.trim() === '') {
licenseUrl = ''; // default to empty string
}
// create the basic publish params
const publishParams = {
name,
file_path: filePath,
bid : publishing.fileClaimBidAmount,
metadata : {
bid: publishing.fileClaimBidAmount,
metadata: {
description,
title,
author : details.title,
author: details.title,
language: 'en',
license,
licenseUrl,
nsfw,
},
claim_address: publishing.primaryClaimAddress,

View file

@ -1,27 +1,28 @@
const logger = require('winston');
const { details, publishing } = require('@config/siteConfig');
const createThumbnailPublishParams = (thumbnailFilePath, claimName, license, nsfw) => {
const createThumbnailPublishParams = (thumbnailFilePath, claimName, license, licenseUrl, nsfw) => {
if (!thumbnailFilePath) {
return;
}
logger.debug(`Creating Thumbnail Publish Parameters`);
// create the publish params
return {
name : `${claimName}-thumb`,
name: `${claimName}-thumb`,
file_path: thumbnailFilePath,
bid : publishing.fileClaimBidAmount,
metadata : {
title : `${claimName} thumbnail`,
bid: publishing.fileClaimBidAmount,
metadata: {
title: `${claimName} thumbnail`,
description: `a thumbnail for ${claimName}`,
author : details.title,
language : 'en',
author: details.title,
language: 'en',
license,
licenseUrl,
nsfw,
},
claim_address: publishing.primaryClaimAddress,
channel_name : publishing.thumbnailChannel,
channel_id : publishing.thumbnailChannelId,
channel_name: publishing.thumbnailChannel,
channel_id: publishing.thumbnailChannelId,
};
};

View file

@ -1,10 +1,15 @@
const logger = require('winston');
const { details: { host }, publishing: { disabled, disabledMessage } } = require('@config/siteConfig');
const {
details: { host },
publishing: { disabled, disabledMessage },
} = require('@config/siteConfig');
const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js');
const isApprovedChannel = require('@globalutils/isApprovedChannel');
const { publishing: { publishOnlyApproved, approvedChannels } } = require('@config/siteConfig');
const {
publishing: { publishOnlyApproved, approvedChannels },
} = require('@config/siteConfig');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
@ -55,6 +60,7 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
fileType,
gaStartTime,
license,
licenseUrl,
name,
nsfw,
thumbnail,
@ -69,18 +75,34 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
// validate the body and files of the request
try {
// validateApiPublishRequest(body, files);
({name, nsfw, license, title, description, thumbnail} = parsePublishApiRequestBody(body));
({fileName, filePath, fileExtension, fileType, thumbnailFileName, thumbnailFilePath, thumbnailFileType} = parsePublishApiRequestFiles(files));
({channelName, channelId, channelPassword} = body);
({
name,
nsfw,
license,
licenseUrl,
title,
description,
thumbnail,
} = parsePublishApiRequestBody(body));
({
fileName,
filePath,
fileExtension,
fileType,
thumbnailFileName,
thumbnailFilePath,
thumbnailFileType,
} = parsePublishApiRequestFiles(files));
({ channelName, channelId, channelPassword } = body);
} catch (error) {
return res.status(400).json({success: false, message: error.message});
return res.status(400).json({ success: false, message: error.message });
}
// check channel authorization
authenticateUser(channelName, channelId, channelPassword, user)
.then(({ channelName, channelClaimId }) => {
if (publishOnlyApproved && !isApprovedChannel({ longId: channelClaimId }, approvedChannels)) {
const error = {
name : UNAPPROVED_CHANNEL,
name: UNAPPROVED_CHANNEL,
message: 'This spee.ch instance only allows publishing to approved channels',
};
throw error;
@ -88,14 +110,25 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
return Promise.all([
checkClaimAvailability(name),
createPublishParams(filePath, name, title, description, license, nsfw, thumbnail, channelName, channelClaimId),
createThumbnailPublishParams(thumbnailFilePath, name, license, nsfw),
createPublishParams(
filePath,
name,
title,
description,
license,
licenseUrl,
nsfw,
thumbnail,
channelName,
channelClaimId
),
createThumbnailPublishParams(thumbnailFilePath, name, license, licenseUrl, nsfw),
]);
})
.then(([ claimAvailable, publishParams, thumbnailPublishParams ]) => {
.then(([claimAvailable, publishParams, thumbnailPublishParams]) => {
if (!claimAvailable) {
const error = {
name : CLAIM_TAKEN,
name: CLAIM_TAKEN,
message: 'That claim name is already taken',
};
throw error;
@ -110,14 +143,20 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
.then(publishResults => {
logger.info('Publish success >', publishResults);
claimData = publishResults;
({claimId} = claimData);
({ claimId } = claimData);
if (channelName) {
return chainquery.claim.queries.getShortClaimIdFromLongClaimId(claimData.certificateId, channelName);
logger.info(`api/claim/publish: claimData.certificateId ${claimData.certificateId}`);
return chainquery.claim.queries.getShortClaimIdFromLongClaimId(
claimData.certificateId,
channelName
);
} else {
return chainquery.claim.queries.getShortClaimIdFromLongClaimId(claimId, name, claimData).catch(() => {
return claimId.slice(0, 1);
});
return chainquery.claim.queries
.getShortClaimIdFromLongClaimId(claimId, name, claimData)
.catch(() => {
return claimId.slice(0, 1);
});
}
})
.then(shortId => {
@ -131,13 +170,13 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
res.status(200).json({
success: true,
message: 'publish completed successfully',
data : {
data: {
name,
claimId,
url : `${host}${canonicalUrl}`, // for backwards compatability with app
showUrl : `${host}${canonicalUrl}`,
url: `${host}${canonicalUrl}`, // for backwards compatability with app
showUrl: `${host}${canonicalUrl}`,
serveUrl: `${host}${canonicalUrl}${fileExtension}`,
pushTo : canonicalUrl,
pushTo: canonicalUrl,
claimData,
},
});

View file

@ -1,15 +1,26 @@
const parsePublishApiRequestBody = ({name, nsfw, license, title, description, thumbnail}) => {
const parsePublishApiRequestBody = ({
name,
nsfw,
license,
licenseUrl,
title,
description,
thumbnail,
}) => {
// validate name
if (!name) {
throw new Error('no name field found in request');
}
const invalidNameCharacters = /[^A-Za-z0-9,-]/.exec(name);
if (invalidNameCharacters) {
throw new Error('The claim name you provided is not allowed. Only the following characters are allowed: A-Z, a-z, 0-9, and "-"');
throw new Error(
'The claim name you provided is not allowed. Only the following characters are allowed: A-Z, a-z, 0-9, and "-"'
);
}
// optional parameters
nsfw = (nsfw === 'true');
nsfw = nsfw === 'true';
license = license || null;
licenseUrl = licenseUrl || null;
title = title || null;
description = description || null;
thumbnail = thumbnail || null;
@ -18,6 +29,7 @@ const parsePublishApiRequestBody = ({name, nsfw, license, title, description, th
name,
nsfw,
license,
licenseUrl,
title,
description,
thumbnail,

View file

@ -19,10 +19,11 @@ const createCanonicalLink = require('@globalutils/createCanonicalLink');
route to update a claim through the daemon
*/
const updateMetadata = ({ nsfw, license, title, description }) => {
const updateMetadata = ({ nsfw, license, licenseUrl, title, description }) => {
const update = {};
if (nsfw) update['nsfw'] = nsfw;
if (license) update['license'] = license;
if (licenseUrl) update['license_url'] = licenseUrl;
if (title) update['title'] = title;
if (description) update['description'] = description;
return update;
@ -65,6 +66,7 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res)
thumbnail,
fileExtension,
license,
licenseUrl,
name,
nsfw,
thumbnailFileName,
@ -127,10 +129,11 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res)
description: claimRecord.description,
nsfw: claimRecord.nsfw,
license: claimRecord.license,
license_url: claimRecord.license_url,
language: 'en',
author: details.title,
},
updateMetadata({ title, description, nsfw, license })
updateMetadata({ title, description, nsfw, license, licenseUrl })
);
const publishParams = {
name,
@ -167,10 +170,12 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res)
}
const fp = files && files.file && files.file.path ? files.file.path : undefined;
logger.info(`before updatepublish`);
return publish(publishParams, fileName, fileType, fp);
})
.then(result => {
publishResult = result;
logger.info(`after updatepublish then`, result);
if (channelName) {
return chainquery.claim.queries.getShortClaimIdFromLongClaimId(

View file

@ -59,5 +59,8 @@ module.exports = async (data, chName = null, chShortId = null) => {
host,
pending: Boolean(dataVals.height === 0),
blocked: blocked,
license: dataVals.license,
licenseUrl: dataVals.license_url,
transactionTime: dataVals.transaction_time,
};
};