Commit bbf5a12b authored by Audrey Hamelers's avatar Audrey Hamelers
Browse files

Merge branch 'dev' into 'master'

Dev

See merge request !180
parents a701735c 00e1cbc1
Pipeline #12848 passed with stages
in 48 seconds
......@@ -65,6 +65,9 @@ const DetailsToggle = styled.div`
}
}
`
const formatName = name =>
`${name.title ? `${name.title} ` : ''}${name.givenNames} ${name.surname}`
const NewNote = ({ close, manuscriptId, message, onChange }) => (
<Mutation
mutation={CREATE_NOTE}
......@@ -214,6 +217,9 @@ class ActivityDetails extends React.Component {
close={() => this.setState({ sendingMail: false })}
currentUser={currentUser}
manuscript={manuscript}
message={`\nKind regards,\n
${formatName(currentUser.identities[0].name)}
Europe PMC Helpdesk`}
refetch={[
{
query: QUERY_ACTIVITY_INFO,
......
......@@ -34,6 +34,7 @@ const NoPadding = styled(Action)`
`
const EmailBody = styled.div`
white-space: pre-wrap;
overflow-wrap: break-word;
padding-top: calc(${th('gridUnit')} * 2);
&,
& div,
......@@ -204,13 +205,22 @@ const ParseFunding = ({ changes, original }) => {
return '(Edited funding - bad data)'
}
const formatName = name =>
`${name.title ? `${name.title} ` : ''}${name.givenNames} ${name.surname}`
class EmailMessage extends React.Component {
state = { open: false, mail: false }
static contextType = UserContext
render() {
const currentUser = this.context
const { email, manuscript, sender } = this.props
const { email, manuscript, sender, time } = this.props
const { open, mail } = this.state
const buildMessage = `\nKind regards,\n
${formatName(currentUser.identities[0].name)}
Europe PMC Helpdesk\n\n
On ${moment(time).format('MM/DD/YYYY HH:mm')} ${formatName(sender)} wrote:
>${email.message.replace(/\n/g, '\n>').replace(/(.{78}[\s])/g, '$1\n>')}
`
return (
<React.Fragment>
<NoteEvent>
......@@ -258,6 +268,7 @@ class EmailMessage extends React.Component {
close={() => this.setState({ mail: false })}
currentUser={currentUser}
manuscript={manuscript}
message={buildMessage}
recipients={[sender.id]}
subject={`Re: ${email.subject}`}
/>
......@@ -283,6 +294,7 @@ const EventDescription = ({ audit, manuscript }) => {
email={content}
manuscript={manuscript}
sender={audit.user}
time={audit.created}
/>
)
}
......
......@@ -5,14 +5,36 @@ import { th } from '@pubsweet/ui-toolkit'
import styled from 'styled-components'
import moment from 'moment'
import { Notification } from '../ui'
import { CHECK_DUPES } from '../operations'
import {
CHECK_DUPES,
CLAIM_MANUSCRIPT,
UNCLAIM_MANUSCRIPT,
} from '../operations'
import { UserContext } from '../App'
import MetaEdit from './MetaEdit'
import { QUERY_ACTIVITY_INFO, LINK_AND_DELETE } from './operations'
const Heading = styled(H2)`
const Heading = styled.div`
border-bottom: ${th('borderWidth')} ${th('borderStyle')} ${th('colorBorder')};
margin: calc(${th('gridUnit')} * 3) 0px calc(${th('gridUnit')} * 2);
padding-bottom: ${th('gridUnit')};
h2 {
display: inline-block;
margin: 0;
}
dl {
display: inline-block;
margin-left: calc(${th('gridUnit')} * 2);
}
`
const ClaimAction = styled(Button)`
font-size: ${th('fontSizeBaseSmall')};
line-height: ${th('fontSize')};
padding: 0 calc(${th('gridUnit')} * 1.5);
margin-left: calc(${th('gridUnit')} * 2);
margin-bottom: 0;
vertical-align: text-bottom;
min-width: 0;
`
const Container = styled.div`
margin: 0 0 calc(${th('gridUnit')} * 2);
......@@ -96,9 +118,11 @@ class MetaSec extends React.Component {
this._mounted = false
clearTimeout(this.notifTimer)
}
static contextType = UserContext
render() {
const { manuscript } = this.props
const { teams, audits, meta, journal } = manuscript
const currentUser = this.context
const { manuscript, claimManuscript, unclaimManuscript } = this.props
const { teams, audits, meta, journal, claiming } = manuscript
const {
fundingGroup,
releaseDelay,
......@@ -168,7 +192,40 @@ class MetaSec extends React.Component {
</Action>
</dd>
</DL>
<Heading>Quick view</Heading>
<Heading>
<H2>Quick view</H2>
{claiming ? (
<DL>
<dt>Claimed:</dt>
<dd>
Helpdesk
{currentUser.id === claiming.id && (
<Action
onClick={async () =>
unclaimManuscript({
variables: { id: manuscript.id },
})
}
>
<Icon color="currentColor" size={1.9}>
x
</Icon>
</Action>
)}
</dd>
</DL>
) : (
<ClaimAction
onClick={async () =>
claimManuscript({
variables: { id: manuscript.id },
})
}
>
Claim
</ClaimAction>
)}
</Heading>
<Container>
{reviewer &&
reviewer.teamMembers[0].user.id ===
......@@ -320,4 +377,20 @@ export default compose(
],
}),
}),
graphql(CLAIM_MANUSCRIPT, {
name: 'claimManuscript',
options: props => ({
refetchQueries: [
{ query: QUERY_ACTIVITY_INFO, variables: { id: props.manuscript.id } },
],
}),
}),
graphql(UNCLAIM_MANUSCRIPT, {
name: 'unclaimManuscript',
options: props => ({
refetchQueries: [
{ query: QUERY_ACTIVITY_INFO, variables: { id: props.manuscript.id } },
],
}),
}),
)(MetaSec)
......@@ -64,6 +64,7 @@ export const QUERY_ACTIVITY_INFO = gql`
fulltextUrl
}
claiming {
id
givenNames
surname
}
......
......@@ -5,7 +5,7 @@ import { th, lighten } from '@pubsweet/ui-toolkit'
import { Action, Button, Icon, Link } from '@pubsweet/ui'
import { B, HTMLString, ZebraList, ZebraListItem, Notification } from '../ui'
import { States, timeSince } from './'
import { CLAIM_MANUSCRIPT, UNCLAIM_MANUSCRIPT } from './operations'
import { CLAIM_MANUSCRIPT, UNCLAIM_MANUSCRIPT } from '../operations'
const ListItem = styled(ZebraListItem)`
display: flex;
......
import gql from 'graphql-tag'
import { ClaimFragment } from '../operations'
const ClaimFragment = gql`
fragment ClaimFragment on Manuscript {
claiming {
id
givenNames
surname
}
}
`
export const CLAIM_MANUSCRIPT = gql`
mutation ClaimManuscript($id: ID!) {
claimManuscript(id: $id) {
manuscript {
id
updated
...ClaimFragment
}
errors {
message
}
}
}
${ClaimFragment}
`
export const UNCLAIM_MANUSCRIPT = gql`
mutation UnclaimManuscript($id: ID!) {
unclaimManuscript(id: $id) {
manuscript {
id
updated
...ClaimFragment
}
errors {
message
}
}
}
${ClaimFragment}
`
export const CURRENT_USER = gql`
query($email: String) {
epmc_currentUser(email: $email) {
......@@ -198,6 +160,7 @@ export const METRICS = gql`
xml_review_within_3_days_perc
published
ncbi_ready_median
external_qa
}
}
`
......@@ -80,6 +80,16 @@ export const ManuscriptFragment = gql`
}
`
export const ClaimFragment = gql`
fragment ClaimFragment on Manuscript {
claiming {
id
givenNames
surname
}
}
`
export const GET_MANUSCRIPT = gql`
query GetManuscript($id: ID!) {
manuscript(id: $id) {
......@@ -163,3 +173,33 @@ export const RECOVER_MANUSCRIPT = gql`
recoverManuscript(id: $id)
}
`
export const CLAIM_MANUSCRIPT = gql`
mutation ClaimManuscript($id: ID!) {
claimManuscript(id: $id) {
manuscript {
id
updated
...ClaimFragment
}
errors {
message
}
}
}
${ClaimFragment}
`
export const UNCLAIM_MANUSCRIPT = gql`
mutation UnclaimManuscript($id: ID!) {
unclaimManuscript(id: $id) {
manuscript {
id
updated
...ClaimFragment
}
errors {
message
}
}
}
${ClaimFragment}
`
......@@ -9,6 +9,7 @@ import styles from '../../assets/epmc_new_plus.scss'
const CodeBox = createGlobalStyle`
#html-preview pre {
white-space: pre-wrap !important;
overflow-wrap: break-word;
border: 0 !important;
margin-top: 0 !important;
font-size: ${th('fontSizeBaseSmall')} !important;
......
......@@ -88,6 +88,7 @@ const PreviewError = styled.div`
padding: ${th('gridUnit')} calc(${th('gridUnit')} * 2);
margin: ${th('gridUnit')} 0;
white-space: pre-wrap;
overflow-wrap: break-word;
`
const ButtonSpacer = styled.div`
height: calc(${th('gridUnit')} * 30) !important;
......
......@@ -39,6 +39,7 @@ const Alert = withTheme(({ children, theme }) => (
const ErrorReport = styled.p`
white-space: pre-wrap;
overflow-wrap: break-word;
font-style: italic;
margin-top: 0;
color: ${th('colorError')};
......
......@@ -31,6 +31,7 @@ export const FileTypes = [
'audio/midi',
'audio/wav',
'audio/ogg',
'audio/x-ms-wma',
'chemical/x-cif', // cif
......@@ -53,6 +54,7 @@ export const FileTypes = [
'video/x-flv', // flv
'video/x-matroska', // mkv
'video/x-ms-asf', // wmv
'video/x-ms-wmv', // wmv
'application/x-troff-msvideo',
'video/avi',
'video/msvideo',
......
......@@ -10,15 +10,15 @@ export default {
display: none;
`,
Selected: css`
border-color: ${th('colorSecondary')};
color: ${th('colorSecondary')};
border-color: ${th('colorSuccess')};
color: ${th('colorSuccess')};
&:hover {
border-color: ${lighten('colorSecondary', 20)};
color: ${lighten('colorSecondary', 20)};
border-color: ${lighten('colorError', 20)};
color: ${lighten('colorError', 20)} !important;
}
&:focus {
outline: none;
color: ${th('colorSecondary')};
color: ${th('colorSuccess')};
box-shadow: ${th('dropShadow')};
}
`,
......
{
"name": "xpub-epmc",
"version": "1.3.1",
"version": "1.3.2",
"private": true,
"description": "xpub configured for Europe PMC Plus manuscript submission system",
"license": "MIT",
......
......@@ -58,7 +58,7 @@ const userMessage = (
regex,
'<a href="$&" style="color:#20699C">$&</a>',
)
const messageDiv = `<div style="white-space:pre-wrap">${linkAdded}</div>`
const messageDiv = `<div style="white-space: pre-wrap;overflow-wrap: break-word;">${linkAdded}</div>`
sendMail(email, subject, messageDiv, from, cc, bcc)
}
......
......@@ -9,7 +9,7 @@ const rejectSubmissionTemplate = (
<p>Dear ${salutation},</p>
<p>Your manuscript submission, <b>${title}</b> was rejected by the reviewer.<p>
<p>The reviewer, ${reviewer}, sent the following message:</p>
<blockquote style="white-space: pre-wrap">${message}</blockquote>
<blockquote style="white-space: pre-wrap;overflow-wrap: break-word;">${message}</blockquote>
<p>Please go to your manuscript at <a style="color:#20699C" href="${link}">${link}</a> to correct any errors in the submission.</p>
<p>Kind regards,</p>
<p>The Europe PMC Helpdesk</p>
......
......@@ -49,7 +49,6 @@ const graphqlClient = new ApolloClient({
link,
})
*/
const ftpUsers = config.get('users')
const ignoreTaggerFolder = `${rootPath}${config.get('ftp_tagger').username}/*`
const watcher = chokidar.watch(`${rootPath}**/*.tar.gz`, {
......@@ -148,18 +147,9 @@ function getUser(rootPath, filename) {
const userName = fileNameParts[rootParts.length - 1]
// We will need the userId from the database for further relating the manuscript to this user.
Identity.findByFtpUsername(userName, '[user]')
.then(identityDb => {
const user = ftpUsers.find(user =>
user.identities
.filter(identity => identity.meta)
.find(identity => identity.meta.ftpUserName === userName),
)
const identity = user.identities
.filter(identity => identity.meta)
.find(identity => identity.meta.ftpUserName === userName)
/* eslint-disable dot-notation */
identity['userId'] = identityDb[0].userId
resolve(identity)
.then(identity => {
if (identity && identity.length > 0) resolve(identity[0])
else resolve(null)
})
.catch(error => reject(error))
})
......
......@@ -11,7 +11,7 @@ module.exports.createGrantLinks = async function createGrantLinks(
) {
try {
const filename = `grants${
manuscripts.length === 1 ? `${manuscripts[0].id}` : ''
manuscripts.length === 1 ? `.${manuscripts[0].id}` : ''
}.${moment().format('YYYY_MM_DD-HH_mm_SS')}.xml`
const sendManuscripts = manuscripts.reduce((list, manuscript) => {
const articleIds =
......
......@@ -69,48 +69,48 @@ module.exports.pushXML = async function pushXML(fileUrl, manuscriptId, userId) {
)
logger.error(errString)
} else {
const nxml = await transformXML(
// Check XML against the stylechecker
const checked = await transformXML(
xml,
path.resolve(__dirname, 'xsl/pnihms2pmc3.xsl'),
path.resolve(__dirname, 'xsl/stylechecker/nlm-stylechecker.xsl'),
)
const nxmlIsWellformed = libxml.parseXml(nxml)
const nxmlIsValid = nxmlIsWellformed.validate(xsdGreenDoc)
const nxmlErrors = nxmlIsWellformed.validationErrors
const result = libxml.parseXml(checked)
if (!nxmlIsValid) {
let errString = 'Invalid NXML: \n\n'
nxmlErrors.forEach((err, i) => {
errString += `${err}\nLine: ${err.line}.`
if (err.str1) {
errString += ` ID: ${err.str1}.`
}
if (i !== nxmlErrors.length - 1) {
errString += `\n\n`
}
})
await Manuscript.update(
{
id: manuscriptId,
formState: errString,
status: 'xml-triage',
pdfDepositState: null,
},
userId,
)
logger.error(errString)
} else {
// Check NXML against the stylechecker
const checked = await transformXML(
nxml,
path.resolve(__dirname, 'xsl/stylechecker/nlm-stylechecker.xsl'),
)
const styleErrors = result.find('//error')
const result = libxml.parseXml(checked)
if (styleErrors.length === 0) {
const nxml = await transformXML(
xml,
path.resolve(__dirname, 'xsl/pnihms2pmc3.xsl'),
)
const styleErrors = result.find('//error')
const nxmlIsWellformed = libxml.parseXml(nxml)
const nxmlIsValid = nxmlIsWellformed.validate(xsdGreenDoc)
const nxmlErrors = nxmlIsWellformed.validationErrors
if (styleErrors.length === 0) {
if (!nxmlIsValid) {
let errString = 'Invalid NXML: \n\n'
nxmlErrors.forEach((err, i) => {
errString += `${err}\nLine: ${err.line}.`
if (err.str1) {
errString += ` ID: ${err.str1}.`
}
if (i !== nxmlErrors.length - 1) {
errString += `\n\n`
}
})
await Manuscript.update(
{
id: manuscriptId,
formState: errString,
status: 'xml-triage',
pdfDepositState: null,
},
userId,
)
logger.error(errString)
} else {
const manuscriptFiles = await File.selectByManuscriptId(manuscriptId)
const filelist = getFilelist(manuscriptFiles)
......@@ -238,25 +238,25 @@ module.exports.pushXML = async function pushXML(fileUrl, manuscriptId, userId) {
logger.info(
'Uploading of nxml and html files to Minio and the database has been completed.',
)
} else {
let styleErrString = `Style Errors: <br/><br/>`
styleErrors.forEach((err, i) => {
styleErrString += err.text()
if (i !== styleErrors.length - 1) {
styleErrString += `<br/><br/>`
}
})
await Manuscript.update(
{
id: manuscriptId,
formState: styleErrString,
status: 'xml-triage',
pdfDepositState: null,
},
userId,
)
logger.error(styleErrString)
}
} else {
let styleErrString = `Style Errors: <br/><br/>`
styleErrors.forEach((err, i) => {
styleErrString += err.text()
if (i !== styleErrors.length - 1) {
styleErrString += `<br/><br/>`
}
})
await Manuscript.update(
{
id: manuscriptId,
formState: styleErrString,
status: 'xml-triage',
pdfDepositState: null,
},
userId,
)
logger.error(styleErrString)
}
}
} catch (err) {
......
......@@ -48,40 +48,39 @@ class Audit extends EpmcBaseModel {
CASE WHEN xml_review=0 THEN 0 ELSE xml_review_within_10_days*100/xml_review END xml_review_within_10_days_perc,
xml_review_within_3_days,
CASE WHEN xml_review=0 THEN 0 ELSE xml_review_within_3_days*100/xml_review END xml_review_within_3_days_perc,
published, ncbi_ready_median
, COALESCE (external_qa, 0) external_qa
published, ncbi_ready_median, COALESCE (external_qa, 0) external_qa
FROM
(SELECT TO_CHAR(date_trunc('month', current_date) + -1*n * INTERVAL '1 month', 'YYYYMM') mth,
TO_CHAR(date_trunc('month', current_date) + -1*n * INTERVAL '1 month', 'Mon YYYY') display_mth,
SUM(CASE WHEN TO_CHAR(d.submitted_date, 'Mon YYYY') = TO_CHAR(date_trunc('month', current_date) + -1*n.n * INTERVAL '1 month', 'Mon YYYY') THEN 1 ELSE 0 END) submitted,
SUM(CASE WHEN TO_CHAR(d.xml_review_date, 'Mon YYYY') = TO_CHAR(date_trunc('month', current_date) + -1*n.n * INTERVAL '1 month', 'Mon YYYY') THEN 1 ELSE 0 END) xml_review,
SUM(CASE WHEN TO_CHAR(d.xml_review_date, 'Mon YYYY') = TO_CHAR(date_trunc('month', current_date) + -1*n.n * INTERVAL '1 month', 'Mon YYYY')
(SELECT TO_CHAR(date_trunc('month', current_date AT TIME ZONE 'Europe/London') + -1*n * INTERVAL '1 month', 'YYYYMM') mth,
TO_CHAR(date_trunc('month', current_date AT TIME ZONE 'Europe/London') + -1*n * INTERVAL '1 month', 'Mon YYYY') display_mth,
SUM(CASE WHEN TO_CHAR(d.submitted_date, 'Mon YYYY') = TO_CHAR(date_trunc('month', current_date AT TIME ZONE 'Europe/London') + -1*n.n * INTERVAL '1 month', 'Mon YYYY') THEN 1 ELSE 0 END) submitted,
SUM(CASE WHEN TO_CHAR(d.xml_review_date, 'Mon YYYY') = TO_CHAR(date_trunc('month', current_date AT TIME ZONE 'Europe/London') + -1*n.n * INTERVAL '1 month', 'Mon YYYY') THEN 1 ELSE 0 END) xml_review,
SUM(CASE WHEN TO_CHAR(d.xml_review_date, 'Mon YYYY') = TO_CHAR(date_trunc('month', current_date AT TIME ZONE 'Europe/London') + -1*n.n * INTERVAL '1 month', 'Mon YYYY')
THEN CASE WHEN DATE_PART('DAY', xml_review_date-submitted_date)<11 THEN 1
ELSE 0
END
ELSE 0
END) xml_review_within_10_days,
SUM(CASE WHEN TO_CHAR(d.xml_review_date, 'Mon YYYY') = TO_CHAR(date_trunc('month', current_date) + -1*n.n * INTERVAL '1 month', 'Mon YYYY')
SUM(CASE WHEN TO_CHAR(d.xml_review_date, 'Mon YYYY') = TO_CHAR(date_trunc('month', current_date AT TIME ZONE 'Europe/London') + -1*n.n * INTERVAL '1 month', 'Mon YYYY')
THEN CASE WHEN DATE_PART('DAY', xml_review_date-submitted_date)<4 THEN 1
ELSE 0
END
ELSE 0
END) xml_review_within_3_days,
SUM(CASE WHEN TO_CHAR(d.first_published_date, 'Mon YYYY') = TO_CHAR(date_trunc('month', current_date) + -1*n.n * INTERVAL '1 month', 'Mon YYYY') THEN 1 ELSE 0 END) published,