Skip to content
Snippets Groups Projects
Commit f6ffa8cb authored by Andy Nicholson's avatar Andy Nicholson
Browse files

fix(*): merged files from main

parents e1d1ae29 0d677da8
No related branches found
No related tags found
No related merge requests found
[Template for request for comments (RFC) on feature proposals]
# RFC: Feature proposal: [Title]
## Context
[Give the necessary context for your proposal. For example, what problem will this feature solve for users? What are the use cases, benefits, and goals?]
## Proposal
[A precise statement of the proposed feature.]
## Design
[Include sketch or wireframes of the UI changes necessary for this feature]
## Implementation (if applicable)
[A description of the steps to implement the feature.]
## Alternative approaches (if applicable)
[Include any alternatives to meet this use case.]
## Open issues (if applicable)
[Links to and a discussion of related issues, if applicable.]
......@@ -2,6 +2,6 @@ module.exports = {
presets: ['@babel/preset-env', '@babel/preset-react'],
plugins: [
'babel-plugin-styled-components',
'@babel/plugin-proposal-class-properties',
['@babel/plugin-proposal-class-properties', { loose: true }],
],
}
......@@ -140,7 +140,8 @@ module.exports = {
theme: process.env.PUBSWEET_THEME,
baseUrl: deferConfig(cfg => {
const { protocol, host, port } = cfg['pubsweet-client']
return `${protocol}://${host}${port ? `:${port}` : ''}`
const hostname = host === '0.0.0.0' ? 'localhost' : host
return `${protocol}://${hostname}${port ? `:${port}` : ''}`
}),
},
'pubsweet-component-xpub-dashboard': {
......
// eslint-disable-next-line no-unused-vars
const { rule, shield, and, or, not, allow, deny } = require('graphql-shield')
const _userIsEditor = async (user, manuscriptId) => {
const userIsEditorQuery = async (user, manuscriptId) => {
if (!user) {
return false
}
......@@ -25,9 +25,9 @@ const _userIsEditor = async (user, manuscriptId) => {
const userIsEditor = rule({
cache: 'contextual',
})(async (parent, args, ctx, info) => _userIsEditor(ctx.user))
})(async (parent, args, ctx, info) => userIsEditorQuery(ctx.user))
const _userIsMemberOfTeamWithRole = async (user, manuscriptId, role) => {
const userIsMemberOfTeamWithRoleQuery = async (user, manuscriptId, role) => {
if (!user) {
return false
}
......@@ -36,6 +36,7 @@ const _userIsMemberOfTeamWithRole = async (user, manuscriptId, role) => {
.$relatedQuery('teams')
.where({ role })
.andWhere({ manuscriptId })
const rows = await query.resultSize()
return !!rows
}
......@@ -44,27 +45,26 @@ const userIsAdmin = rule({ cache: 'contextual' })(
async (parent, args, ctx, info) => ctx.user && ctx.user.admin,
)
const parent_manuscript_is_published = rule({ cache: 'contextual' })(
const parentManuscriptIsPublished = rule({ cache: 'contextual' })(
async (parent, args, ctx, info) => {
const manuscript = await ctx.models.Manuscript.query().findById(
parent.manuscriptId,
)
return !!manuscript.published
},
)
const review_is_by_user = rule({ cache: 'contextual' })(
const reviewIsByUser = rule({ cache: 'contextual' })(
async (parent, args, ctx, info) => {
const rows =
ctx.user &&
ctx.user
.$relatedQuery('teams')
.where({ role: 'reviewer' })
.resultSize()
ctx.user.$relatedQuery('teams').where({ role: 'reviewer' }).resultSize()
return !!rows
},
)
const isAuthenticated = rule({ cache: 'contextual' })(
async (parent, args, ctx, info) => !!ctx.user,
)
......@@ -78,35 +78,44 @@ const userIsAllowedToChat = rule({ cache: 'strict' })(
if (ctx.user && ctx.user.admin) {
return true
}
const channel = await ctx.models.Channel.query().findById(args.channelId)
const manuscript = await ctx.models.Manuscript.query().findById(
channel.manuscriptId,
)
const isAuthor = await _userIsMemberOfTeamWithRole(
const isAuthor = await userIsMemberOfTeamWithRoleQuery(
ctx.user,
manuscript.id,
'author',
)
const isReviewer = await _userIsMemberOfTeamWithRole(
const isReviewer = await userIsMemberOfTeamWithRoleQuery(
ctx.user,
manuscript.id,
'reviewer',
)
const isEditor = await _userIsEditor(ctx.user, manuscript.id)
const isEditor = await userIsEditorQuery(ctx.user, manuscript.id)
if (channel.type === 'all') {
return isAuthor || isReviewer || isEditor
} else if (channel.type === 'editorial') {
}
if (channel.type === 'editorial') {
return isReviewer || isEditor
}
return false
},
)
const user_is_review_author_and_review_is_not_completed = rule({
const userIsReviewAuthorAndReviewIsNotCompleted = rule({
cache: 'strict',
})(async (parent, args, ctx, info) => {
let manuscriptId
if (args.id) {
;({ manuscriptId } = await ctx.models.Review.query().findById(args.id))
} else {
......@@ -114,12 +123,14 @@ const user_is_review_author_and_review_is_not_completed = rule({
}
const manuscript = await ctx.models.Manuscript.query().findById(manuscriptId)
const team = await ctx.models.Team.query()
.where({
manuscriptId: manuscript.id,
role: 'reviewer',
})
.first()
if (!team) return false
const members = await team
......@@ -133,22 +144,24 @@ const user_is_review_author_and_review_is_not_completed = rule({
return false
})
const user_is_editor_of_the_manuscript_of_the_review = rule({
const userIsEditorOfTheManuscriptOfTheReview = rule({
cache: 'strict',
})(async (parent, args, ctx, info) => {
let manuscriptId
if (args.id) {
;({ manuscriptId } = await ctx.models.Review.query().findById(args.id))
} else {
;({ manuscriptId } = args.input)
}
return _userIsEditor(ctx.user, manuscriptId)
return userIsEditorQuery(ctx.user, manuscriptId)
})
const user_is_invited_reviewer = rule({ cache: 'strict' })(
const userIsInvitedReviewer = rule({ cache: 'strict' })(
async (parent, args, ctx, info) => {
const team = await ctx.models.Team.query().findById(args.teamId)
const member = await team
.$relatedQuery('members')
.where({ userId: ctx.user.id, status: 'invited' })
......@@ -158,7 +171,7 @@ const user_is_invited_reviewer = rule({ cache: 'strict' })(
},
)
const user_is_author = rule({ cache: 'strict' })(
const userIsAuthor = rule({ cache: 'strict' })(
async (parent, args, ctx, info) => {
const team = await ctx.models.Team.query()
.where({
......@@ -166,6 +179,7 @@ const user_is_author = rule({ cache: 'strict' })(
role: 'author',
})
.first()
const author = team
.$relatedQuery('members')
.where({ userId: ctx.user.id })
......@@ -175,10 +189,11 @@ const user_is_author = rule({ cache: 'strict' })(
},
)
const user_is_author_of_files_associated_manuscript = rule({
const userIsAuthorOfFilesAssociatedManuscript = rule({
cache: 'no_cache',
})(async (parent, args, ctx, info) => {
let manuscriptId
if (args.meta && args.meta.manuscriptId) {
// Meta is supplied for createFile
// eslint-disable-next-line prefer-destructuring
......@@ -202,6 +217,7 @@ const user_is_author_of_files_associated_manuscript = rule({
if (!team) {
return false
}
const members = await team
.$relatedQuery('members')
.where('userId', ctx.user.id)
......@@ -212,8 +228,13 @@ const user_is_author_of_files_associated_manuscript = rule({
return false
})
const user_is_author_of_the_manuscript_of_the_file = rule({ cache: 'strict' })(
const userIsAuthorOfTheManuscriptOfTheFile = rule({ cache: 'strict' })(
async (parent, args, ctx, info) => {
if (!ctx.user) {
return false
}
const manuscript = await ctx.models.File.relatedQuery('manuscript')
.for(parent.id)
.first()
......@@ -228,6 +249,7 @@ const user_is_author_of_the_manuscript_of_the_file = rule({ cache: 'strict' })(
if (!team) {
return false
}
const members = await team
.$relatedQuery('members')
.where('userId', ctx.user.id)
......@@ -241,11 +263,13 @@ const user_is_author_of_the_manuscript_of_the_file = rule({ cache: 'strict' })(
)
// ¯\_(ツ)_/¯
const user_is_the_reviewer_of_the_manuscript_of_the_file_and_review_not_complete = rule(
{
cache: 'strict',
},
)(async (parent, args, ctx, info) => {
const userIsTheReviewerOfTheManuscriptOfTheFileAndReviewNotComplete = rule({
cache: 'strict',
})(async (parent, args, ctx, info) => {
if (!ctx.user) {
return false
}
const manuscript = await ctx.models.File.relatedQuery('manuscript')
.for(parent.id)
.first()
......@@ -260,6 +284,7 @@ const user_is_the_reviewer_of_the_manuscript_of_the_file_and_review_not_complete
if (!team) {
return false
}
const members = await team
.$relatedQuery('members')
.where('userId', ctx.user.id)
......@@ -286,21 +311,21 @@ const permissions = {
Mutation: {
upload: isAuthenticated,
createManuscript: isAuthenticated,
updateManuscript: user_is_author,
submitManuscript: user_is_author,
updateManuscript: userIsAuthor,
submitManuscript: userIsAuthor,
createMessage: userIsAllowedToChat,
updateReview: or(
user_is_review_author_and_review_is_not_completed,
user_is_editor_of_the_manuscript_of_the_review,
userIsReviewAuthorAndReviewIsNotCompleted,
userIsEditorOfTheManuscriptOfTheReview,
),
reviewerResponse: user_is_invited_reviewer,
reviewerResponse: userIsInvitedReviewer,
completeReview: or(
user_is_review_author_and_review_is_not_completed,
user_is_editor_of_the_manuscript_of_the_review,
userIsReviewAuthorAndReviewIsNotCompleted,
userIsEditorOfTheManuscriptOfTheReview,
),
createNewVersion: allow,
createFile: user_is_author_of_files_associated_manuscript,
deleteFile: user_is_author_of_files_associated_manuscript,
createFile: userIsAuthorOfFilesAssociatedManuscript,
deleteFile: userIsAuthorOfFilesAssociatedManuscript,
},
Subscription: {
messageCreated: userIsAllowedToChat,
......@@ -314,16 +339,16 @@ const permissions = {
Manuscript: allow,
ManuscriptVersion: allow,
File: or(
parent_manuscript_is_published,
parentManuscriptIsPublished,
or(
user_is_author_of_the_manuscript_of_the_file,
user_is_the_reviewer_of_the_manuscript_of_the_file_and_review_not_complete,
userIsAuthorOfTheManuscriptOfTheFile,
userIsTheReviewerOfTheManuscriptOfTheFileAndReviewNotComplete,
userIsEditor,
userIsAdmin,
),
),
UploadResult: allow,
Review: or(parent_manuscript_is_published, review_is_by_user),
Review: or(parentManuscriptIsPublished, reviewIsByUser),
ReviewComment: allow,
Channel: allow,
Message: allow,
......@@ -337,10 +362,11 @@ const permissions = {
const fallbackRule = or(userIsAdmin, userIsEditor)
// We only ever need to go two levels down, so no need for recursion
const addOverrideRule = permissions => {
const addOverrideRule = perms => {
const adaptedPermissions = {}
Object.keys(permissions).forEach(key1 => {
const value = permissions[key1]
Object.keys(perms).forEach(key1 => {
const value = perms[key1]
if (value.constructor.name !== 'Object') {
adaptedPermissions[key1] = or(fallbackRule, value)
} else {
......
......@@ -14,10 +14,11 @@ services:
'./webpack/webpack.development.config.js',
]
ports:
- ${CLIENT_PORT:-4000}:8080
- ${CLIENT_PORT:-4000}:${CLIENT_PORT:-4000}
environment:
- NODE_ENV=development
- CLIENT_HOST=0.0.0.0
- CLIENT_PORT=${CLIENT_PORT:-4000}
- SERVER_PROTOCOL=http
- SERVER_HOST=server
- SERVER_PORT=3000
......
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
const Btn = styled.button`
background-color: ${props => props.primary && 'blue'};
font-family: ${props => props.theme.fontInterface};
`
/** This appears in storybook */
const Button = ({ label, primary }) => <Btn primary={primary}>{label}</Btn>
Button.propTypes = {
/** This also appears in storybook */
label: PropTypes.string.isRequired,
/** As props table documentation */
primary: PropTypes.bool,
}
Button.defaultProps = {
primary: false,
}
export default Button
import React from 'react'
import Button from './FakeButton'
export const Base = args => <Button {...args} />
Base.args = {
label: 'Click me',
primary: false,
}
export const Primary = () => <Button label="Primary" primary />
export const NonPrimary = () => <Button label="Not primary" />
export default {
title: 'Fake Section/Button',
component: Button,
argTypes: {
label: { control: { type: 'text' } },
primary: { control: { type: 'boolean' } },
},
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment