Skip to content
Snippets Groups Projects
Commit 1fd25057 authored by Audrey Hamelers's avatar Audrey Hamelers
Browse files

WIP: add new reviewer

parent e317bb12
No related branches found
No related tags found
2 merge requests!60Dev,!41Shared data model
Showing
with 232 additions and 201 deletions
import React from 'react'
import { Query } from 'react-apollo'
import { Query, compose, graphql } from 'react-apollo'
import { H4 } from '@pubsweet/ui'
import { Loading, LoadingIcon, Notification } from '../ui'
import { ALL_MANUSCRIPTS, ADMIN_MANUSCRIPTS } from './operations'
import { ALL_MANUSCRIPTS, ADMIN_MANUSCRIPTS, ADD_REVIEWER } from './operations'
import DashboardBase from './DashboardBase'
import DashboardList from './DashboardList'
import SearchBoxes from './SearchBoxes'
......@@ -161,23 +161,35 @@ const MyManuscripts = ({ currentUser, history }) => (
const QueuePage = DashboardBase(MyQueue)
const ManuscriptsPage = DashboardBase(MyManuscripts)
const DashboardPage = ({ currentUser, history }) => {
if (currentUser.admin) {
class DashboardPage extends React.Component {
/* componentDidMount() {
const { search } = this.props.location
if (search) {
const noteId = search.split('=')[1]
this.props.addReviewer(noteId)
}
} */
render() {
const { currentUser, history } = this.props
if (currentUser.admin) {
return (
<QueuePage
currentUser={currentUser}
history={history}
pageTitle="My queue"
/>
)
}
return (
<QueuePage
<ManuscriptsPage
currentUser={currentUser}
history={history}
pageTitle="My queue"
pageTitle="My manuscripts"
/>
)
}
return (
<ManuscriptsPage
currentUser={currentUser}
history={history}
pageTitle="My manuscripts"
/>
)
}
export default DashboardPage
export default compose(
graphql(ADD_REVIEWER, { name: 'addReviewer' }),
)(DashboardPage)
......@@ -46,6 +46,11 @@ export const CURRENT_USER = gql`
}
}
`
export const ADD_REVIEWER = gql`
mutation AddReviewer($noteId: ID!) {
addReviewer(noteId: $noteId)
}
`
export const COUNT_MANUSCRIPTS = gql`
query CountByStatus {
countByStatus {
......
......@@ -57,6 +57,7 @@ class Login extends React.Component {
handleSubmit,
signup = true,
passwordReset = true,
location,
} = this.props
return (
<Page>
......@@ -85,7 +86,7 @@ class Login extends React.Component {
{signup && (
<Signup>
{`Don’t have a Europe PMC plus account? `}
<Link to="/signup">Create an account.</Link>
<Link to={`/signup${location.search}`}>Create an account.</Link>
</Signup>
)}
{passwordReset && (
......
......@@ -16,8 +16,8 @@ const handleSubmit = (values, { props, setSubmitting, setErrors }) =>
const { search } = props.location
let path = ''
if (search && search.includes('?next=')) {
path = search.split('=')[1]
? search.split('=')[1]
path = search.split(/=(.+)/)[1]
? search.split(/=(.+)/)[1]
: redirectPath({ location: props.location })
}
createHistory({ forceRefresh: true }).push(path)
......
......@@ -111,7 +111,7 @@ class Signup extends React.Component {
/>
)
render() {
const { values, errors, handleSubmit, touched } = this.props
const { values, errors, handleSubmit, touched, location } = this.props
return (
<Page>
<H1>Create a Europe PMC plus account</H1>
......@@ -182,7 +182,7 @@ class Signup extends React.Component {
</form>
<SignIn>
{`Already have a Europe PMC plus account? `}
<Link to="/login">Sign in.</Link>
<Link to={`/login${location.search}`}>Sign in.</Link>
</SignIn>
<SignInFooter />
</Page>
......
......@@ -36,7 +36,9 @@ const handleSubmit = (
}),
)
setTimeout(() => {
createHistory({ forceRefresh: true }).push('/login')
createHistory({ forceRefresh: true }).push(
`/login${values.location.search}`,
)
}, 100)
setSubmitting(true)
}
......@@ -63,6 +65,7 @@ const enhancedFormik = withFormik({
surname: props.surname,
confirmPassword: props.confirmPassword,
signup: true,
location: props.location,
}),
validationSchema: yup.object().shape({
email: yup
......
......@@ -16,69 +16,67 @@ export const GET_USER = gql`
}
`
export const GET_MANUSCRIPT = gql`
query GetManuscript($id: ID!) {
manuscript(id: $id) {
id
status
formState
journal {
journalTitle
meta {
nlmta
pmcStatus
pubmedStatus
}
}
const ManuscriptFragment = gql`
fragment ManuscriptFragment on Manuscript {
id
status
formState
journal {
journalTitle
meta {
nlmta
pmcStatus
pubmedStatus
}
}
meta {
title
articleIds {
pubIdType
id
}
publicationDates {
type
date
}
fundingGroup {
fundingSource
awardId
title
articleIds {
pubIdType
id
}
publicationDates {
type
date
}
fundingGroup {
fundingSource
awardId
pi {
surname
givenNames
title
pi {
surname
givenNames
title
email
}
}
releaseDelay
unmatchedJournal
notes {
id
notesType
content
email
}
}
files {
releaseDelay
unmatchedJournal
notes {
id
type
label
filename
url
mimeType
notesType
content
}
teams {
role
teamMembers {
user {
id
}
alias {
name {
title
surname
givenNames
}
}
files {
id
type
label
filename
url
mimeType
}
teams {
role
teamMembers {
user {
id
}
alias {
name {
title
surname
givenNames
}
}
}
......@@ -86,6 +84,15 @@ export const GET_MANUSCRIPT = gql`
}
`
export const GET_MANUSCRIPT = gql`
query GetManuscript($id: ID!) {
manuscript(id: $id) {
...ManuscriptFragment
}
}
${ManuscriptFragment}
`
export const CREATE_MANUSCRIPT = gql`
mutation CreateManuscript {
createManuscript {
......@@ -161,61 +168,13 @@ export const UPDATE_NOTE = gql`
export const SUBMIT_MANUSCRIPT = gql`
mutation SubmitManuscript($data: ManuscriptInput!) {
submitManuscript(data: $data) {
id
status
formState
meta {
title
articleIds {
pubIdType
id
}
publicationDates {
type
date
}
fundingGroup {
fundingSource
awardId
title
pi {
surname
givenNames
title
email
}
}
releaseDelay
unmatchedJournal
notes {
id
notesType
content
}
manuscript {
...ManuscriptFragment
}
files {
id
type
label
filename
url
mimeType
}
teams {
role
teamMembers {
user {
id
}
alias {
name {
title
surname
givenNames
}
}
}
errors {
message
}
}
}
${ManuscriptFragment}
`
const config = require('config')
const Email = require('@pubsweet/component-send-email')
const { htmlEmailBase } = require('./templates')
const {
htmlEmailBase,
setReviewerTemplate,
newReviewerTemplate,
} = require('./templates')
const { testAddress } = config['epmc-email']
const { sender, url, testAddress } = config['epmc-email']
const sendMail = (email, subject, messageInHtml) => {
const mailData = {
from: config.get('mailer.from'),
from: sender,
to: testAddress || email,
subject,
subject: `[Europe PMC plus] ${subject}`,
html: htmlEmailBase(messageInHtml),
}
Email.send(mailData)
}
const reviewerEmail = ({ reviewer, manInfo, submitter, token }) => {
const { title, givenNames, surname } = submitter
const submitterName = `${title ? `${title} ` : ''}${givenNames} ${surname}`
const { title: t, givenNames: g, surname: s } = reviewer.name
const salutation = `${t ? `${t} ` : ''}${g} ${s}`
let html = ''
if (reviewer.id) {
const link = `${url}submission/${manInfo.id}/submit`
html = setReviewerTemplate(salutation, manInfo.title, submitterName, link)
} else {
const link = `${url}dashboard?claim=${token}`
html = newReviewerTemplate(salutation, manInfo.title, submitterName, link)
}
const subject = `Approve submission of ${manInfo.id}`
sendMail(reviewer.email, subject, html)
}
module.exports = {
sendMail,
reviewerEmail,
}
const { resetPasswordTemplate } = require('./resetPassword')
const { newReviewerTemplate, reviewerSetTemplate } = require('./reviewerAdded')
const { newReviewerTemplate, setReviewerTemplate } = require('./reviewerAdded')
const htmlEmailBase = content => `<body style="height: 100%;margin: 0;padding: 0;width: 100%;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table id="bodyTable" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 0;width: 100%;background-color: #EEF6FC;" width="100%" height="100%" cellspacing="0" cellpadding="0" border="0" align="center">
......@@ -61,5 +61,5 @@ module.exports = {
htmlEmailBase,
resetPasswordTemplate,
newReviewerTemplate,
reviewerSetTemplate,
setReviewerTemplate,
}
const newReviewerTemplate = (salutation, title, submitter, link) => `
<h1 style="font-weight:600;">Review manuscript submission</h1>
<p>Dear ${salutation},</p>
<p>A manuscript, ${title}, was submitted to Europe PMC on your behalf by ${submitter}.</p>
<p>A manuscript, <b>${title}</b>, was submitted to Europe PMC on your behalf by ${submitter}.</p>
<p>You have been made the reviewer of this manuscript, which means you are responsible for ensuring the content of the submission is correct and for approving the final web version.</p>
<p>Please <a style="color:#20699C" href="${link}">log in to or create</a> a Europe PMC account to claim this manuscript for review.</p>
<p>Kind regards,</p>
......@@ -11,7 +11,7 @@ const newReviewerTemplate = (salutation, title, submitter, link) => `
const setReviewerTemplate = (salutation, title, submitter, link) => `
<h1 style="font-weight:600;">Review manuscript submission</h1>
<p>Dear ${salutation},</p>
<p>A manuscript, ${title}, was submitted to Europe PMC on your behalf by ${submitter}.</p>
<p>A manuscript, <b>${title}</b>, was submitted to Europe PMC on your behalf by ${submitter}.</p>
<p>You have been made the reviewer of this manuscript, which means you are responsible for ensuring the content of the submission is correct and for approving the final web version.</p>
<p>Please visit the following link to log in to your account and review the submission:<br/><a style="color:#20699C" href="${link}">${link}</a></p>
<p>Kind regards,</p>
......
......@@ -224,25 +224,17 @@ class Manuscript extends EpmcBaseModel {
.where('manuscript.id', id)
.whereNull('manuscript.deleted')
.eager('[journal, teams.users, notes, files, claiming]')
.modifyEager('notes', builder => {
builder.whereNull('deleted')
})
.modifyEager('files', builder => {
builder.whereNull('deleted')
})
} else {
manuscripts = await Manuscript.query()
.where('id', id)
.whereNull('deleted')
}
if (manuscripts && manuscripts[0] && manuscripts[0].files) {
manuscripts[0].files = manuscripts[0].files.filter(file => !file.deleted)
manuscripts[0].files.sort((a, b) => {
const aName = a.filename.toLowerCase()
const bName = b.filename.toLowerCase()
if (aName < bName) {
return -1
}
if (aName > bName) {
return 1
}
return 0
})
}
return manuscripts && manuscripts.length > 0 ? manuscripts[0] : null
}
......
const { transaction } = require('objection')
const config = require('config')
const lodash = require('lodash')
const rfr = require('rfr')
const authorization = require('pubsweet-server/src/helpers/authorization')
const logger = require('@pubsweet/logger')
const EmailValidator = require('email-validator')
......@@ -8,9 +9,12 @@ const ManuscriptAccess = require('./data-access')
const FileAccess = require('../file/data-access')
const NoteAccess = require('../note/data-access')
const ReviewAccess = require('../review/data-access')
const UserAccess = require('../user/data-access')
const Team = require('../team/data-access')
const { dManuscriptUpdate, gManuscript } = require('./helpers/transform')
const { reviewerEmail } = rfr('server/email')
const mergeObjects = (...inputs) =>
lodash.mergeWith(
...inputs,
......@@ -200,19 +204,34 @@ const Manuscript = {
submit: async (input, userId) => {
const originalMan = await ManuscriptAccess.selectById(input.id)
const errors = []
if (!originalMan) {
throw new Error('Manuscript not found')
errors.push({ message: 'Manuscript not found' })
return {
manuscript: null,
errors,
}
}
if (input.status === 'submitted') {
const notes = await NoteAccess.selectByManuscriptId(input.id)
const reviewerNote = notes.find(n => n.notesType === 'selectedReviewer')
if (reviewerNote) {
let trx
try {
trx = await transaction.start(Team.knex())
const reviewer = JSON.parse(reviewerNote.content)
if (reviewer.id) {
const submitter = await UserAccess.findById(userId)
const reviewer = JSON.parse(reviewerNote.content)
const emailObject = {
reviewer,
manInfo: {
id: input.id,
title: originalMan['meta,title'],
},
submitter,
token: reviewerNote.id,
}
if (reviewer.id) {
let trx
try {
trx = await transaction.start(Team.knex())
const team = new Team({
manuscriptId: input.id,
userId: reviewer.id,
......@@ -220,24 +239,32 @@ const Manuscript = {
updatedBy: userId,
})
await team.saveWithTrx(trx)
const teams = await Team.selectByManuscriptId(input.id)
const submitter = teams.find(t => t.roleName === 'submitter')
if (reviewer.id !== submitter.userId) {
input.status = 'in-review'
await NoteAccess.delete(reviewerNote.id, userId)
await trx.commit()
} catch (error) {
if (trx) {
await trx.rollback()
}
logger.error('Nothing was created: ', error)
errors.push(error)
return {
manuscript: null,
errors,
}
} else {
throw new Error('ERROR: user does not have account')
// TODO: Create user account & invitation for new reviewer!!
}
await trx.commit()
// TODO: Send email to reviewers who are not the submitter!!
NoteAccess.delete(reviewerNote.id, userId)
} catch (error) {
if (trx) {
await trx.rollback()
if (reviewer.id !== submitter.userId) {
input.status = 'in-review'
await reviewerEmail(emailObject)
}
logger.error('Nothing was created: ', error)
throw error
} else {
input.status = 'in-review'
await reviewerEmail(emailObject)
}
} else {
errors.push({ message: 'Reviewer must be selected' })
return {
manuscript: null,
errors,
}
}
}
......@@ -246,7 +273,10 @@ const Manuscript = {
lodash.assign(originalMan, manuscriptUpdate)
await originalMan.save()
const updatedMan = await ManuscriptAccess.selectById(input.id, true)
return gManuscript(updatedMan)
return {
manuscript: gManuscript(updatedMan),
errors,
}
},
applyInput: (originalManuscript, input) => {
......
const Team = require('./data-access')
const Note = require('../note/data-access')
const TeamManager = {
find: Team.selectById,
findByUserIdOrManuscriptId: Team.selectByUserIdOrManuscriptId,
selectByUserId: async id => Team.selectByUserId(id),
selectByManuscriptId: async id => Team.selectByManuscriptId(id),
addNewReviewer: async (noteId, userId) => {
const note = Note.selectById(noteId)
const { manuscriptId } = note
await Team.insert(
{
manuscriptId,
userId,
roleName: 'reviewer',
},
userId,
)
return true
},
delete: Team.delete,
save: async (team, userId) => {
let { id } = team
......
......@@ -14,32 +14,13 @@ const resolvers = {
if (!user) {
throw new Error('You are not authenticated!')
}
const email = await UserManager.findEmail(userId)
sendMail(email, subject, messageInHtml)
await sendMail(email, subject, messageInHtml)
return true
},
/* async epmc_addReviewer(_, { reviewer, manInfo, submitterName, teamId }) {
const { title, givenNames, surname } = reviewer.name
const salutation = `${title ? title + ' ':''}${givenNames} ${surname}`
let html = ''
if (reviewer.id) {
const link = `${url}/submission/${manInfo.id}/submit`
html = htmlEmailBase(setReviewerTemplate(
salutation,
manInfo.title,
submitterName,
link,
))
} else {
const link = `${url}/login/?claim=${teamId}`
html = htmlEmailBase(newReviewerTemplate(
salutation,
manInfo.title,
submitterName,
link,
))
}
/* async epmc_addReviewer(_, data, { user }) {
await reviewerEmail(data)
return true
}, */
async epmc_emailPasswordResetLink(_, { email }, ctx) {
const user = await UserManager.findByEmail(email)
......@@ -50,9 +31,9 @@ const resolvers = {
email,
id: user.id,
})
const passwordResetLink = `${url}/password-reset/${token}`
const passwordResetLink = `${url}password-reset/${token}`
sendMail(
await sendMail(
email,
'Europe PMC Plus - Reset Password',
resetPasswordTemplate(user, passwordResetLink),
......
......@@ -32,7 +32,7 @@ extend type Mutation {
createManuscript: Manuscript!
deleteManuscript(id: ID!): Boolean!
updateManuscript(data: ManuscriptInput!): Manuscript!
submitManuscript(data: ManuscriptInput!): Manuscript!
submitManuscript(data: ManuscriptInput!): ManuscriptResult!
claimManuscript(id: ID!): ManuscriptResult!
unclaimManuscript(id: ID!): ManuscriptResult!
savePage(url:String!, id:String!):Manuscript!
......
......@@ -12,6 +12,15 @@ const resolvers = {
return teamList
},
},
Mutation: {
async addReviewer(_, { noteId }, user) {
if (!user) {
throw new Error('You are not authenticated!')
}
await TeamManager.addNewReviewer(noteId, user)
return true
},
},
}
module.exports = resolvers
extend type Query {
userTeams(id: ID!): [Team]!
}
extend type Mutation {
addReviewer(noteId: ID!): Boolean!
}
\ No newline at end of file
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