diff --git a/packages/component-mail-service/src/Mail.js b/packages/component-mail-service/src/Mail.js index afae0f54739c116e5b7f0e80a9ad0902b90be6b5..aa1feda200bfa5114f7054b2bc2db3c473edaaf3 100644 --- a/packages/component-mail-service/src/Mail.js +++ b/packages/component-mail-service/src/Mail.js @@ -145,6 +145,9 @@ module.exports = { invitationId: meta.invitation.id, agree: true, } + const manuscriptDetailsLink = `/projects/${meta.collection.id}/versions/${ + meta.fragment.id + }/details` const declineUrl = helpers.createUrl(baseUrl, resetPasswordPath, { ...queryParams, @@ -155,7 +158,7 @@ module.exports = { let agreeUrl = helpers.createUrl( baseUrl, - `/projects/${meta.collection.id}/versions/${meta.fragment.id}/details`, + manuscriptDetailsLink, queryParams, ) @@ -173,7 +176,7 @@ module.exports = { const replacements = { agreeUrl, declineUrl, - baseUrl, + manuscriptDetailsUrl: helpers.createUrl(baseUrl, manuscriptDetailsLink), title: meta.fragment.title, authors: meta.fragment.authors, abstract: meta.fragment.abstract, @@ -392,6 +395,25 @@ module.exports = { replacements.signatureName }` break + + case 'no-response-reviewers-after-recommendation': + subject = meta.emailSubject + replacements.hasLink = false + replacements.previewText = 'a manuscript has reached a decision' + replacements.intro = `Dear Dr. ${user.firstName} ${user.lastName}` + + replacements.paragraph = `An editorial decision has been made regarding manuscript "${ + meta.fragment.title + }" by ${ + meta.fragment.authorName + }. So, you do not need to proceed with the review of this manuscript. <br/> + If you have comments on this manuscript you believe the Editor should see, please email them to Hindawi as soon as possible.` + delete replacements.detailsUrl + replacements.signatureName = meta.handlingEditorName + textBody = `${replacements.intro} ${replacements.paragraph} ${ + replacements.signatureName + }` + break case 'eic-recommendation': subject = `${meta.collection.customId}: Manuscript Recommendation` replacements.previewText = diff --git a/packages/component-mail-service/src/helpers/helpers.js b/packages/component-mail-service/src/helpers/helpers.js index f0a4bbb75ef25ac5752789dde16246c358826441..886226a5619717ca4da72e7dc07f93353a7aa9ff 100644 --- a/packages/component-mail-service/src/helpers/helpers.js +++ b/packages/component-mail-service/src/helpers/helpers.js @@ -80,7 +80,7 @@ const getInvitationBody = (emailType, replacements) => { handlePartial('signature', signatureContext) const lowerContext = { - baseUrl: replacements.baseUrl, + manuscriptDetailsUrl: replacements.manuscriptDetailsUrl, lowerContent: replacements.lowerContent, signature: replacements.signature, } diff --git a/packages/component-mail-service/src/templates/partials/invitationLowerContent.hbs b/packages/component-mail-service/src/templates/partials/invitationLowerContent.hbs index 6d5e3fd25468819a8f248a3f893d4d42c11f9d0d..ef60e6b2aa6d9d303515dfc6feead9c054c30cc6 100644 --- a/packages/component-mail-service/src/templates/partials/invitationLowerContent.hbs +++ b/packages/component-mail-service/src/templates/partials/invitationLowerContent.hbs @@ -2,7 +2,7 @@ <tr> <td style="padding:30px 23px 0px 23px;background-color:#ffffff;" height="100%" valign="top" bgcolor="#ffffff"> <p data-pm-slice="1 1 []"> - <a href="{{baseUrl}}">See more information</a> + <a href="{{ manuscriptDetailsUrl }}">See more information</a> </p> <p data-pm-slice="1 1 []"> </p> diff --git a/packages/component-mail-service/src/templates/partials/invitationUpperContent.hbs b/packages/component-mail-service/src/templates/partials/invitationUpperContent.hbs index 8dfd58f9bdc81e037d72cd96fcc90f0e302a6720..1bacf2e791241303b5242c62b5f2393a6dc43930 100644 --- a/packages/component-mail-service/src/templates/partials/invitationUpperContent.hbs +++ b/packages/component-mail-service/src/templates/partials/invitationUpperContent.hbs @@ -2,7 +2,7 @@ <tr> <td style="padding:30px 23px 20px 23px;background-color:#ffffff;" height="100%" valign="top" bgcolor="#ffffff"> <div> - <p data-pm-slice="1 1 []">{{ intro}},</p> + <p data-pm-slice="1 1 []">{{ intro }},</p> <p> </p> <p>{{ upperContent }}</p> diff --git a/packages/component-manuscript-manager/src/helpers/User.js b/packages/component-manuscript-manager/src/helpers/User.js index c5e4e828fc293389ac2bd96f2e3299e6ff0c00be..a67ba27d3829787fffbfe39a3f888a8a4b1a110a 100644 --- a/packages/component-manuscript-manager/src/helpers/User.js +++ b/packages/component-manuscript-manager/src/helpers/User.js @@ -84,6 +84,38 @@ const setupAgreedReviewersEmailData = async ({ ) } +const setupNoResponseReviewersEmailData = async ({ + baseUrl, + fragment: { title, authorName }, + collection, + mailService, + UserModel, +}) => { + const invitations = collection.invitations.filter( + inv => inv.role === 'reviewer' && inv.hasAnswer === false, + ) + const userPromises = await invitations.map(async inv => + UserModel.find(inv.userId), + ) + let users = await Promise.all(userPromises) + users = users.filter(Boolean) + const subject = `${collection.customId}: Reviewer Unassigned` + users.forEach(user => + mailService.sendNotificationEmail({ + toEmail: user.email, + user, + emailType: 'no-response-reviewers-after-recommendation', + meta: { + collection: { customId: collection.customId }, + fragment: { title, authorName }, + handlingEditorName: collection.handlingEditor.name, + baseUrl, + emailSubject: subject, + }, + }), + ) +} + const setupEiCRecommendationEmailData = async ({ baseUrl, UserModel, @@ -184,4 +216,5 @@ module.exports = { setupAgreedReviewersEmailData, setupEiCRecommendationEmailData, setupAuthorRequestToRevisionEmailData, + setupNoResponseReviewersEmailData, } diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js index 2ac0d2a6348357d8487768976ad7edd84145afb0..d75ec539d9a4f334af75f14dbe568da4e9ceca7c 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js @@ -80,6 +80,13 @@ module.exports = models => async (req, res) => { fragment: { title, authorName }, recommendation, }) + await userHelper.setupNoResponseReviewersEmailData({ + baseUrl, + UserModel: models.User, + collection, + mailService, + fragment: { title, authorName }, + }) await userHelper.setupEiCRecommendationEmailData({ baseUrl, UserModel: models.User, diff --git a/packages/xpub-faraday/config/authsome-helpers.js b/packages/xpub-faraday/config/authsome-helpers.js index 3604124b1a8f1a7a6259bed680f0d23b4742377d..1b7642bfca5445c4c1651d8cbfcca8aad45ef8c5 100644 --- a/packages/xpub-faraday/config/authsome-helpers.js +++ b/packages/xpub-faraday/config/authsome-helpers.js @@ -60,12 +60,7 @@ const filterObjectData = ( return object } -const getTeamsByPermissions = async ( - teamIds = [], - permissions, - TeamModel, - object, -) => { +const getTeamsByPermissions = async (teamIds = [], permissions, TeamModel) => { const teams = await Promise.all( teamIds.map(async teamId => { const team = await TeamModel.find(teamId) diff --git a/packages/xpub-faraday/config/authsome-mode.js b/packages/xpub-faraday/config/authsome-mode.js index df0eb9dcc76c2235bf5e9736c6bf668b53f7646f..fbbea3d6a4401ca8752656b9b80ef5eeca1aa7c3 100644 --- a/packages/xpub-faraday/config/authsome-mode.js +++ b/packages/xpub-faraday/config/authsome-mode.js @@ -9,7 +9,6 @@ async function teamPermissions(user, operation, object, context) { user.teams, permissions, context.models.Team, - object, ) let collectionsPermissions = await Promise.all( @@ -20,8 +19,12 @@ async function teamPermissions(user, operation, object, context) { permission: team.teamType.permissions, } const objectType = get(object, 'type') - if (objectType === 'fragment' && collection.fragments.includes(object.id)) - collPerm.fragmentId = object.id + if (objectType === 'fragment') { + if (collection.fragments.includes(object.id)) + collPerm.fragmentId = object.id + else return null + } + if (objectType === 'collection') if (object.id !== collection.id) return null return collPerm @@ -111,14 +114,13 @@ async function authenticatedUser(user, operation, object, context) { return true } - // Allow the authenticated user to GET collections they own - if (operation === 'GET' && object === '/collections/') { - return { - filter: collection => collection.owners.includes(user.id), + // allow authenticate owners full pass for a collection + if (get(object, 'type') === 'collection') { + if (operation === 'PATCH') { + return { + filter: collection => omit(collection, 'filtered'), + } } - } - - if (operation === 'GET' && get(object, 'type') === 'collection') { if (object.owners.includes(user.id)) return true const owner = object.owners.find(own => own.id === user.id) if (owner !== undefined) return true @@ -198,7 +200,7 @@ async function authenticatedUser(user, operation, object, context) { return false } - if (user.teams.length !== 0 && operation === 'GET') { + if (user.teams.length !== 0 && ['GET'].includes(operation)) { const permissions = await teamPermissions(user, operation, object, context) if (permissions) { @@ -216,19 +218,6 @@ async function authenticatedUser(user, operation, object, context) { } } - if (get(object, 'type') === 'collection') { - if (['GET', 'DELETE'].includes(operation)) { - return true - } - - // Only allow filtered updating (mirroring filtered creation) for non-admin users) - if (operation === 'PATCH') { - return { - filter: collection => omit(collection, 'filtered'), - } - } - } - // A user can GET, DELETE and PATCH itself if (get(object, 'type') === 'user' && get(object, 'id') === user.id) { if (['GET', 'DELETE', 'PATCH'].includes(operation)) { diff --git a/packages/xpub-faraday/config/default.js b/packages/xpub-faraday/config/default.js index c70ca459bfe9bb249ba55d410deb00d3ddff747c..d0206232d69f270f4b2d6bd45277a4795f276321 100644 --- a/packages/xpub-faraday/config/default.js +++ b/packages/xpub-faraday/config/default.js @@ -79,7 +79,7 @@ module.exports = { }, }, mailer: { - from: 'hindawi@thinslices.com', + from: 'faraday@hindawi.com', path: `${__dirname}/mailer`, }, publicKeys: [