Commit f7519c40 authored by Audrey Hamelers's avatar Audrey Hamelers

Merge branch 'dev' into 'master'

Dev

See merge request !185
parents 1ec4c4f9 c67c4855
Pipeline #13097 passed with stages
in 48 seconds
......@@ -113,6 +113,13 @@
margin-top: 1em;
}
hr {
height: 1px;
background-color: #ccc;
border: 0;
margin: 2em auto;
}
ol, ul {
margin-left: 1em;
padding-left: 1em;
......@@ -264,6 +271,10 @@
display: inline-block;
}
.inline {
display: inline;
}
.overflow-hidden {
overflow: hidden;
}
......
......@@ -187,6 +187,7 @@ class PDFViewer extends React.Component {
<Viewer
fitWidth={this.setFit}
loading={loading}
maxPages={this.props.maxPages}
onScroll={this.countPages}
pdf={pdf}
scale={scale}
......
......@@ -14,7 +14,7 @@ const Pane = styled.div`
class Viewer extends React.PureComponent {
render() {
const { pdf, loading, ...props } = this.props
const { pdf, loading, maxPages, ...props } = this.props
const numPages = pdf && pdf._pdfInfo ? pdf._pdfInfo.numPages : 0
return (
<Pane className="pdf-viewer-pane" onScroll={props.onScroll}>
......@@ -30,7 +30,7 @@ class Viewer extends React.PureComponent {
/>
) : (
<React.Fragment>
{numPages < 51 ? (
{!maxPages || numPages < maxPages ? (
[...Array(numPages)].map((v, i) => (
<Page
index={i + 1}
......@@ -43,7 +43,7 @@ class Viewer extends React.PureComponent {
))
) : (
<React.Fragment>
{[...Array(51)].map((v, i) => (
{[...Array(maxPages + 1)].map((v, i) => (
<Page
index={i + 1}
key={`document-page-${Math.round(
......
......@@ -55,7 +55,13 @@ class ManuscriptPreview extends React.Component {
</Doc>
)
} else if (file.mimeType === 'application/pdf') {
return <PDFViewer textContent={this.props.textContent} url={file.url} />
return (
<PDFViewer
maxPages={50}
textContent={this.props.textContent}
url={file.url}
/>
)
}
return (
<Notification type="info">
......
......@@ -165,14 +165,16 @@ class MetaSec extends React.Component {
return log
}, {})
const year =
(publicationDates &&
publicationDates.find(d => d.type === 'ppub') &&
(publicationDates.find(d => d.type === 'ppub').jatsDate
? publicationDates.find(d => d.type === 'ppub').jatsDate.year
: moment(
publicationDates.find(d => d.type === 'ppub').date,
).year())) ||
null
publicationDates &&
publicationDates.reduce((retYear, date) => {
const getYear = date.jatsDate
? date.jatsDate.year
: moment(date.date).year()
if (date.type === 'ppub' || !retYear) {
return getYear
}
return retYear
}, null)
const { edit, notif } = this.state
return (
<React.Fragment>
......
......@@ -116,7 +116,12 @@ const QuickView = ({ manuscript }) => (
</Col>
<Col flex={1}>
<div>Published</div>
<Status current={manuscript.status} mark="ncbi-ready" />
<Status
current={manuscript.status}
mark={
manuscript.status === 'ncbi-triage' ? 'ncbi-triage' : 'ncbi-ready'
}
/>
</Col>
</Checklist>
</React.Fragment>
......
......@@ -20,6 +20,7 @@ const HelpdeskQueue = {
'needs XML QA': ['xml-qa'],
'has XML errors': ['xml-triage'],
'needs citation': ['xml-complete'],
'failed NCBI loading': ['ncbi-triage'],
}
const SubmitterQueue = {
......
......@@ -26,7 +26,7 @@ class DashboardPage extends React.Component {
if (this.state.errors.length > 0) {
setTimeout(() => {
this.setState({ errors: [] })
}, 4000)
}, 5000)
}
}
async setReviewer(noteId, newPath) {
......@@ -36,25 +36,15 @@ class DashboardPage extends React.Component {
awaitRefetchQueries: true,
})
this.props.history.replace(newPath)
if (!set.data.addReviewer) {
this.setState({
errors: [
{
message: 'Manuscript already accepted for review.',
type: 'warning',
},
],
})
} else {
this.setState({
errors: [
{
message: 'Manuscript accepted for review.',
type: 'success',
},
],
})
}
const { success, message } = await set.data.addReviewer
this.setState({
errors: [
{
message,
type: success ? 'success' : 'warning',
},
],
})
}
render() {
const { currentUser, history } = this.props
......
......@@ -54,6 +54,11 @@ const submitterState = {
color: 'success',
url: 'submit',
},
'ncbi-triage': {
status: 'Approved for Europe PMC',
color: 'success',
url: 'submit',
},
published: {
status: 'Available in Europe PMC',
color: 'success',
......@@ -122,6 +127,11 @@ const reviewerState = {
color: 'success',
url: 'submit',
},
'ncbi-triage': {
status: 'Approved for Europe PMC',
color: 'success',
url: 'submit',
},
published: {
status: 'Available in Europe PMC',
color: 'success',
......@@ -190,6 +200,11 @@ const adminState = {
color: 'success',
url: 'activity',
},
'ncbi-triage': {
status: 'NCBI errors',
color: 'error',
url: 'review',
},
published: {
status: 'Available in Europe PMC',
color: 'success',
......
......@@ -10,7 +10,10 @@ export const CURRENT_USER = gql`
`
export const ADD_REVIEWER = gql`
mutation AddReviewer($noteId: ID!) {
addReviewer(noteId: $noteId)
addReviewer(noteId: $noteId) {
success
message
}
}
`
export const COUNT_MANUSCRIPTS = gql`
......
......@@ -36,7 +36,17 @@ const PasswordReset = ({ errors, handleSubmit, touched, status }) => (
)}
{!isEmpty(errors) && typeof errors === 'string' && (
<Notification type="error">{errors}</Notification>
<Notification type="error">
{errors === 'jwt expired' ? (
<React.Fragment>
{`Your password reset link has expired. Please `}
<Link to="/password-reset">reset your password</Link>
{` again.`}
</React.Fragment>
) : (
errors
)}
</Notification>
)}
{status && status.reset && (
......
......@@ -135,23 +135,24 @@ class Review extends React.Component {
}
componentDidMount() {
if (this.page && this.props.manuscript) {
if (
this.props.manuscript.files &&
!this.props.manuscript.files.find(f => f.type === 'PMC')
) {
const { files, status } = this.props.manuscript
if (files && !files.find(f => f.type === 'PMC')) {
this.setState({ showAll: true })
}
if (
['xml-triage', 'tagging'].includes(this.props.manuscript.status) &&
['xml-triage', 'tagging', 'ncbi-triage'].includes(status) &&
this.props.currentUser.admin
) {
this.setState({ pane: 'files', open: false })
this.setState({ pane: 'files', open: status === 'ncbi-triage' })
}
}
}
showSuccess = () =>
new Promise((resolve, reject) => {
if (this.page) {
window.scrollY = 0
window.pageYOffset = 0
document.scrollingElement.scrollTop = 0
this.setState({ success: true })
setTimeout(() => {
this.setState({ success: false }, resolve())
......@@ -228,12 +229,16 @@ class Review extends React.Component {
>
<PreviewPanelDiv style={{ maxWidth: showManuscript && '1000px' }}>
<PreviewPanelHeader>
<H1>
{(status === 'xml-triage' && 'Correct') ||
(status === 'tagging' && 'Tag') ||
'Review'}
{` final web versions`}
</H1>
{status === 'ncbi-triage' ? (
<H1>Fix NCBI loading errors</H1>
) : (
<H1>
{(status === 'xml-triage' && 'Correct') ||
(status === 'tagging' && 'Tag') ||
'Review'}
{` final web versions`}
</H1>
)}
</PreviewPanelHeader>
{pane !== 'files' && (
<Instructions>
......@@ -252,7 +257,7 @@ class Review extends React.Component {
XML files
</Action>
)}
{['xml-qa', 'xml-triage'].includes(status) && (
{['xml-qa', 'xml-triage', 'ncbi-triage'].includes(status) && (
<Action
className={pane === 'xml' ? 'current' : ''}
disabled={!xml}
......
......@@ -62,7 +62,13 @@ class ReviewFooter extends React.Component {
Send for XML QA
</Button>
)
} else if (manuscript.status === 'xml-triage') {
} else if (status === 'ncbi-triage') {
return (
<Button onClick={() => setStatus('xml-complete')} primary>
Retry send to NCBI
</Button>
)
} else if (status === 'xml-triage') {
const options = [
{
value: 'xml-qa',
......@@ -72,10 +78,6 @@ class ReviewFooter extends React.Component {
value: 'xml-review',
label: 'Author review',
},
{
value: 'xml-complete',
label: 'PMC (skip review)',
},
]
return (
<div>
......
......@@ -58,7 +58,11 @@ const ReviewFormDisplay = ({
{annotations && <ListErrors annotations={annotations} />}
{deleted.length > 0 && <ReviewHistory reviews={deleted} />}
<p>
{`You can fix any errors and approve for review, or send this list of issues to the taggers to fix. You may edit the annotation text to provide more detail if necessary.`}
{`You can fix any errors and approve for review, or ${
review
? 'send this list of issues to the taggers to fix. You may edit the annotation text to provide more detail if necessary'
: 'request a fix from the taggers'
}.`}
</p>
<p>
{`If an error is caused by missing or incomplete files, click 'Return for user upload' and you will be given the opportunity to write a message explaining what is needed, and send the manuscript back to the submitter or reviewer to upload files.`}
......@@ -87,6 +91,8 @@ const ReviewFormDisplay = ({
)
}
return null
} else if (status === 'ncbi-triage') {
return null
}
history.push('/')
return null
......
......@@ -116,7 +116,12 @@ class Submit extends React.Component {
this.setState({ editing: null })
}
pruneDupes = () => this.setState({ prune: true })
showSuccess = () => this.setState({ success: true })
showSuccess = () => {
window.scrollY = 0
window.pageYOffset = 0
document.scrollingElement.scrollTop = 0
this.setState({ success: true })
}
render() {
const { currentUser, manuscript, duplicates: checkDupes = [] } = this.props
const {
......
......@@ -10,6 +10,7 @@
"xml-review",
"xml-complete",
"ncbi-ready",
"ncbi-triage",
"published",
"being-withdrawn"
]
\ No newline at end of file
{
"name": "xpub-epmc",
"version": "1.4.0",
"version": "1.5.0",
"private": true,
"description": "xpub configured for Europe PMC Plus manuscript submission system",
"license": "MIT",
......
const resetPasswordTemplate = ({ title, givenNames, surname }, link) => `
<h1 style="font-weight:600;">Reset your password</h1>
<p>Dear ${title ? `${title} ` : ''}${givenNames} ${surname},</p>
<p>Please click the link to <a style="color:#20699C" href="${link}">reset the password</a> of your Europe PMC Plus account.</p>
<p>Please click the link to <a style="color:#20699C" href="${link}">reset the password</a> of your Europe PMC Plus account. This link will expire in 24 hours.</p>
<p>If you did not request a password reset, please disregard this message.</p>
<p>Kind regards,</p>
<p>The Europe PMC Helpdesk</p>
......
......@@ -159,7 +159,7 @@ async function updateManuscriptNcbiStatus(fileName, response) {
)
if (response.status) {
manInput.formState = `Failed NCBI loading\n\n${response.formState}`
manInput.status = 'xml-triage'
manInput.status = 'ncbi-triage'
} else if (response.pmcid) {
const newPMCID = response.pmcid.startsWith('PMC')
? response.pmcid
......
......@@ -127,7 +127,7 @@ async function selectResult(results, manuscripts) {
if (
manuscript.status === 'xml-complete' &&
citationData.meta &&
citationData.meta.volume
(citationData.meta.volume || citationData.meta.issue)
) {
citationData.status = 'ncbi-ready'
}
......
......@@ -326,7 +326,7 @@ function retryNcbiApiCall(
if (attempt <= retries) {
logger.error(`retry attempt: ${attempt} - ${manuscript.id}`)
manuscriptModel.update(
{ id: manuscript.id, retryAttempt: attempt },
{ id: manuscript.id, formState: err, retryAttempt: attempt },
adminUser.id,
)
attempt += 1
......@@ -340,36 +340,64 @@ function retryNcbiApiCall(
)
}
reject(err)
// update formState column with error message
await manuscriptModel.update(
{
id: manuscript.id,
formState: err,
},
adminUser.id,
)
}
})
}
async function deposit(manuscript, adminUser) {
if (manuscript.files) {
const nxml =
manuscript.files &&
manuscript.files.find(file => !file.deleted && file.type === 'PMCfinal')
const files =
manuscript.files &&
manuscript.files.filter(file => !file.deleted && file.type === 'IMGprint')
const bigfiles = files.filter(file => file.size > 20000000)
if (bigfiles.length > 0) {
const err = `Unable to generate PDF. The following TIFF files are too large:\n${bigfiles
.map(f => f.filename)
.join('\n')}`
logger.error(err)
await manuscriptModel.update(
{
id: manuscript.id,
formState: err,
pdfDepositState: null,
status:
manuscript.status === 'tagging' ? 'xml-triage' : manuscript.status,
},
adminUser.id,
)
} else if (nxml) {
logger.info(`Start depositing ${manuscript.id}`)
files.push(nxml)
const depositObj = {
action: 'commit',
depositor: 'ebi',
domain: 'ukpmcpa',
items: [],
}
manuscript.files
.filter(
file =>
!file.deleted &&
(file.type === 'PMCfinal' || file.type === 'IMGprint'),
)
.forEach(file => {
depositObj.items.push({
item: `${baseUrl}${file.url}`,
item_size: file.size,
})
})
depositObj.items = files.map(file => ({
item: `${baseUrl}${file.url}`,
item_size: file.size,
}))
// retry method(adminUser, attempt_number, manuscriptObj, depositObj)
await retryNcbiApiCall(adminUser, 1, manuscript, depositObj)
} else {
logger.info(`No files to deposit for manuscript ${manuscript.id}`)
logger.error(`No files to deposit for manuscript ${manuscript.id}`)
await manuscriptModel.update(
{
id: manuscript.id,
formState: 'No NXML file to deposit for PDF conversion',
},
adminUser.id,
)
}
}
......
......@@ -19,6 +19,8 @@ xslt4node.addLibrary('./saxon9he.jar')
xslt4node.addOptions('-Xmx1g')
module.exports.pushXML = async function pushXML(fileUrl, manuscriptId, userId) {
const man = await Manuscript.findById(manuscriptId, userId)
const changeStatus = ['tagging', 'xml-qa'].includes(man.status)
try {
await Manuscript.update(
{ id: manuscriptId, formState: null, pdfDepositState: 'CREATING_NXML' },
......@@ -62,7 +64,7 @@ module.exports.pushXML = async function pushXML(fileUrl, manuscriptId, userId) {
{
id: manuscriptId,
formState: errString,
status: 'xml-triage',
status: changeStatus ? 'xml-triage' : man.status,
pdfDepositState: null,
},
userId,
......@@ -104,7 +106,7 @@ module.exports.pushXML = async function pushXML(fileUrl, manuscriptId, userId) {
{
id: manuscriptId,
formState: errString,
status: 'xml-triage',
status: changeStatus ? 'xml-triage' : man.status,
pdfDepositState: null,
},
userId,
......@@ -227,7 +229,7 @@ module.exports.pushXML = async function pushXML(fileUrl, manuscriptId, userId) {
{
id: manuscriptId,
formState: errString,
status: 'xml-triage',
status: changeStatus ? 'xml-triage' : man.status,
},
userId,
)
......@@ -251,7 +253,7 @@ module.exports.pushXML = async function pushXML(fileUrl, manuscriptId, userId) {
{
id: manuscriptId,
formState: styleErrString,
status: 'xml-triage',
status: changeStatus ? 'xml-triage' : man.status,
pdfDepositState: null,
},
userId,
......@@ -264,7 +266,7 @@ module.exports.pushXML = async function pushXML(fileUrl, manuscriptId, userId) {
{
id: manuscriptId,
formState: err.message,
status: 'xml-triage',
status: changeStatus ? 'xml-triage' : man.status,
pdfDepositState: null,
},
userId,
......
......@@ -24,6 +24,22 @@
</xsl:template>
<xsl:variable name="emsid" select="translate(translate(//article-meta/article-id[@pub-id-type = 'manuscript'], 'ucpak', 'es'), 'ems', 'EMS')"/>
<xsl:variable name="fn-symbols" select="'*†‡§‖¶'"/>
<xsl:template name="get-symbol">
<xsl:param name="count"/>
<xsl:param name="current"/>
<xsl:variable name="times" select="ceiling($count div 6)"/>
<xsl:variable name="symbol" select="substring($fn-symbols, $count mod 6, 1)"/>
<xsl:value-of select="$symbol"/>
<xsl:if test="$current &lt; $times">
<xsl:call-template name="get-symbol">
<xsl:with-param name="count" select="$count"/>
<xsl:with-param name="current" select="$current + 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="/">
<!--<html>
......@@ -260,7 +276,7 @@
<div class="abs_link_metadata">
<div>
<xsl:for-each select="//article-meta//contrib-group/contrib[@contrib-type = 'author']">
<div class="inline-block">
<div class="inline">
<!--<xsl:if test="descendant::name">
<div style="display: none;" id="authspan{count(preceding-sibling::contrib)+1}">
<div class="author-refine-panel">
......@@ -448,8 +464,18 @@
</xsl:for-each>
</xsl:variable>
<xsl:template match="name" mode="authorlist">
<a href="{concat('https://europepmc.org/search?query=AUTH:%22', surname, '+', substring(given-names, 1, 1),'%22')}" data-target="authspan{count(parent::contrib/preceding-sibling::contrib)+1}">
<xsl:template match="name|collab" mode="authorlist">
<a data-target="authspan{count(parent::contrib/preceding-sibling::contrib)+1}">
<xsl:attribute name="href">
<xsl:choose>
<xsl:when test="self::name">
<xsl:value-of select="concat('https://europepmc.org/search?query=AUTH:%22', surname, '+', substring(given-names, 1, 1),'%22')"/>
</xsl:when>
<xsl:when test="self::collab">
<xsl:value-of select="concat('/search?query=AUTH:%22', translate(., ' ', '+'), '%22')"/>
</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="class">
<xsl:text>abstract--affiliation-group</xsl:text>
<xsl:for-each select="following-sibling::xref[@ref-type = 'aff']">
......@@ -472,7 +498,14 @@
</xsl:for-each>
</xsl:attribute>
<span class="abstract--author-name">
<xsl:value-of select="concat(given-names, ' ', surname)"/>
<xsl:choose>
<xsl:when test="self::name">
<xsl:value-of select="concat(given-names, ' ', surname)"/>
</xsl:when>
<xsl:when test="self::collab">
<xsl:value-of select="."/>
</xsl:when>
</xsl:choose>
</span>
<xsl:for-each select="following-sibling::aff">
<xsl:variable name="current" select="."/>
......@@ -528,17 +561,34 @@
<sup class="inline-block">#</sup>
</a>
</xsl:if>
<xsl:for-each select="following-sibling::xref[@ref-type = 'fn']">
<xsl:variable name="rid" select="@rid"/>
<xsl:for-each select="//fn[@id = $rid]">
<xsl:if test="not(@fn-type) or (@fn-type != 'con' and @fn-type != 'equal' and @fn-type != 'present-address')">
<xsl:variable name="count" select="count(preceding-sibling::fn[not(@fn-type)] |
preceding-sibling::fn[(@fn-type != 'con' and @fn-type != 'equal' and @fn-type != 'present-address')]) + 1"/>
<a href="{concat('#', $rid)}">
<sup class="inline-block">
<xsl:call-template name="get-symbol">
<xsl:with-param name="count" select="$count"/>
<xsl:with-param name="current" select="1"/>
</xsl:call-template>
</sup>
</a>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
<xsl:variable name="corresp" select="following-sibling::xref[@ref-type = 'corresp']/@rid"/>
<xsl:choose>
<xsl:when test="following-sibling::corresp//email">
<!--<i class="fa fa-envelope author-refine-icon"/>-->
<a href="#{$corresp}">
<sup class="inline-block">&#9993;</sup>
<sup class="inline-block"><big>&#9993;</big></sup>
</a>
</xsl:when>
<xsl:when test="//corresp[@id = $corresp]">
<a href="#{$corresp}">
<sup class="inline-block">&#9993;</sup>
<sup class="inline-block"><big>&#9993;</big></sup>
</a>
<!--<xsl:choose>
<xsl:when test="count(//corresp[@id = $corresp]//email) = 1">
......@@ -553,7 +603,7 @@
</xsl:when>
<xsl:when test="following-sibling::corresp">
<a href="#{$corresp}">
<sup class="inline-block">&#9993;</sup>
<sup class="inline-block"><big>&#9993;</big></sup>
</a>
<!--<a href="#author-notes">
<i class="fa fa-envelope author-refine-icon"/>
......@@ -562,12 +612,6 @@
</xsl:choose>
</xsl:template>
<xsl:template match="collab" mode="authorlist">
<a href="{concat('/search?query=AUTH:%22', translate(., ' ', '+'), '%22')}">
<xsl:value-of select="."/>
</a>
</xsl:template>
<xsl:template match="aff" mode="afflist">
<xsl:variable name="alpha" select="'abcdefghijklmnopqrstuvwxyz'"/>
<li>
......@@ -1356,12 +1400,7 @@
<!-- Handle Table FootNote -->
<xsl:template match="table-wrap-foot">
<div class="table-foot">
<ul class="table-footnotes">
<xsl:if test="descendant::label">
<xsl:attribute name="style">
<xsl:text>list-style-type:none;</xsl:text>
</xsl:attribute>
</xsl:if>
<ul class="table-footnotes" style="list-style-type:none;">
<xsl:apply-templates/>
</ul>
</div>
......@@ -1566,6 +1605,9 @@
<!--<div class="elife-article-decision-letter">
<xsl:apply-templates select="*[not(self::supplementary-material)]"/>
</div>-->
<xsl:if test="child::*[position()=1][self::p]">
<hr />
</xsl:if>
<div id="main-text">
<div class="article fulltext-view">
<xsl:apply-templates mode="testing" select="*[not(@sec-type = 'supplementary-material')][not(@sec-type = 'floats-group')]"/>
......@@ -2039,7 +2081,14 @@
</xsl:template>
<xsl:template match="author-notes/fn[not(@fn-type)]/p | author-notes/fn[(@fn-type != 'con' and @fn-type != 'equal' and @fn-type != 'present-address')]/p">
<xsl:variable name="count" select="count(parent::fn/preceding-sibling::fn[not(@fn-type)] |
preceding-sibling::fn[(@fn-type != 'con' and @fn-type != 'equal' and @fn-type != 'present-address')]) + 1"/>
<p>
<xsl:call-template name="get-symbol">