diff --git a/packages/component-admin/.gitignore b/packages/component-admin/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3614a810088d89d9ccaa28d82401545634874a18 --- /dev/null +++ b/packages/component-admin/.gitignore @@ -0,0 +1,8 @@ +_build/ +api/ +logs/ +node_modules/ +uploads/ +.env.* +.env +config/local*.* \ No newline at end of file diff --git a/packages/component-admin/README.md b/packages/component-admin/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6107c057207697467a41159bcc3f0eee270ae181 --- /dev/null +++ b/packages/component-admin/README.md @@ -0,0 +1,42 @@ +# AWS SES + +In order to use `component-aws-ses` you first need to have a `.env` file containing AWS data in the root folder of the starting point of your application. + +The `.env` file contain the following data: +```bash +AWS_SES_SECRET_KEY = <secretKey> +AWS_SES_ACCESS_KEY = <accessKey> +EMAIL_SENDER = verified_ses_sender@domain.com +AWS_SES_REGION = region-name +``` + +Then, as soon as possible in your app you should add the `dotenv` package: +```js +require('dotenv').config() +``` + +# `component-aws-ses` API +A list of endpoints that help you upload, download and delete S3 files. + +## Send an email [POST] +#### Request +`POST /api/email` +#### Request body + +All parameters are `required` +```json +{ + "email": "to_email@domain.com", + "subject": "Example subject", + "textBody": "this is an email", + "htmlBody": "<p><b>This</b> is an <i>email</i>" +} +``` +#### Response +```json +HTTP/1.1 204 +``` + + + + diff --git a/packages/component-admin/index.js b/packages/component-admin/index.js new file mode 100644 index 0000000000000000000000000000000000000000..f569b273abd8938c1a205c421dff09fc32e824ec --- /dev/null +++ b/packages/component-admin/index.js @@ -0,0 +1,3 @@ +module.exports = { + backend: () => app => require('./src/User')(app), +} diff --git a/packages/component-admin/package.json b/packages/component-admin/package.json new file mode 100644 index 0000000000000000000000000000000000000000..12bc8f99650bf87df42887c5d9d350090b7d8771 --- /dev/null +++ b/packages/component-admin/package.json @@ -0,0 +1,29 @@ +{ + "name": "pubsweet-component-admin", + "version": "0.0.1", + "description": "admin component for pubsweet", + "license": "MIT", + "files": [ + "src" + ], + "scripts": { + "test": "jest" + }, + "repository": { + "type": "git", + "url": "https://gitlab.coko.foundation/xpub/xpub" + }, + "dependencies": { + "body-parser": "^1.17.2" + }, + "peerDependencies": { + "@pubsweet/logger": "^0.0.1", + "pubsweet": "^1.1.1", + "pubsweet-client": "^1.1.1", + "pubsweet-server": "^1.0.1" + }, + "devDependencies": { + "jest": "^22.1.1", + "supertest": "^3.0.0" + } +} diff --git a/packages/component-admin/src/User.js b/packages/component-admin/src/User.js new file mode 100644 index 0000000000000000000000000000000000000000..f72f01649615103c983a34407c02ebd314a4596e --- /dev/null +++ b/packages/component-admin/src/User.js @@ -0,0 +1,150 @@ +const bodyParser = require('body-parser') +const logger = require('@pubsweet/logger') +const uuid = require('uuid') +const crypto = require('crypto') +const mailService = require('pubsweet-component-mail-service') + +const User = app => { + app.use(bodyParser.json()) + const authBearer = app.locals.passport.authenticate('bearer', { + session: false, + }) + app.post('/api/admin/users', authBearer, async (req, res) => { + const reqUser = await app.locals.models.User.find(req.user) + if (reqUser.admin !== true) { + res.status(403).json({ error: 'Unauthorized' }) + logger.error('unauthorized request') + return + } + const { email, role } = req.body + if (email === undefined || role === undefined) { + res.status(400).json({ error: 'all parameters are required' }) + logger.error('some parameters are missing') + return + } + + try { + const user = await app.locals.models.User.findByEmail(email) + if (user) { + res.status(400).json({ error: 'User already exists' }) + logger.error('admin tried to invite existing user') + return + } + } catch (e) { + if (e.name === 'NotFoundError') { + const userBody = { + username: uuid.v4().slice(0, 7), + email, + password: uuid.v4(), + roles: { role }, + passwordResetToken: crypto.randomBytes(32).toString('hex'), + isConfirmed: false, + } + let newUser = new app.locals.models.User(userBody) + newUser = await newUser.save() + let emailType + switch (newUser.roles.role) { + case 'editorInChief': + emailType = 'invite-editor-in-chief' + break + case 'handlingEditor': + emailType = 'invite-handling-editor' + break + case 'reviewer': + emailType = 'invite-reviewer' + break + default: + break + } + await mailService.setupEmail( + newUser.email, + emailType, + newUser.passwordResetToken, + ) + } + } + + res.status(204).json() + }) + app.post( + '/api/admin/users/password-reset', + bodyParser.json(), + async (req, res) => { + const { + token, + password, + email, + firstName, + lastName, + username, + middleName, + affiliation, + position, + title, + } = req.body + + if ( + !checkForUndefinedParams( + token, + password, + email, + firstName, + lastName, + username, + ) + ) { + res.status(400).json({ error: 'missing required params' }) + return + } + + const updateFields = { + password, + firstName, + lastName, + username, + middleName, + affiliation, + position, + title, + isConfirmed: true, + } + + try { + const user = await app.locals.models.User.findByEmail(email) + if (user) { + if (token !== user.passwordResetToken) { + res.status(400).json({ error: 'invalid request' }) + logger.error('admin pw reset tokens do not match') + return + } + + let newUser = Object.assign(user, updateFields, user) + delete newUser.passwordResetToken + + newUser = await newUser.save() + res.status(200).json(newUser) + } + } catch (e) { + if (e.name === 'NotFoundError') { + res.status(404).json({ error: 'user not found' }) + logger.error('admin pw reset on non-existing user') + } else if (e.name === 'ValidationError') { + res.status(400).json({ error: e.details[0].message }) + logger.error('admin pw reset validation error') + } + res.status(400).json({ error: e }) + logger.error(e) + } + }, + ) +} + +const checkForUndefinedParams = (...params) => { + if (params.includes(undefined)) { + return false + } + + return true +} + +module.exports = User diff --git a/packages/component-aws-ses/index.js b/packages/component-aws-ses/index.js index 8b8d52ceca5a0d380e5e5a3a66681030ccfad18a..b54639e30fd890a999ba73bcf5ab581788aeaa82 100644 --- a/packages/component-aws-ses/index.js +++ b/packages/component-aws-ses/index.js @@ -1,5 +1,3 @@ require('dotenv').config() -module.exports = { - backend: () => app => require('./src/EmailBackend')(app), -} +module.exports = require('./src/EmailBackend') diff --git a/packages/component-aws-ses/package.json b/packages/component-aws-ses/package.json index 6267c1f10a75287b9b72fe5e0c2f369e2f6b8190..21500be0578aca6d2dfa147f4e3e5ddee0829738 100644 --- a/packages/component-aws-ses/package.json +++ b/packages/component-aws-ses/package.json @@ -1,6 +1,6 @@ { "name": "pubsweet-components-aws-ses", - "version": "0.0.1", + "version": "0.2.0", "description": "xpub aws ses configured for faraday", "license": "MIT", "files": [ diff --git a/packages/component-aws-ses/src/EmailBackend.js b/packages/component-aws-ses/src/EmailBackend.js index 33a52e0f83f0b42874701cfdfece5ff793a5805a..3cef31414720ec8cb8caf745fd84f3df4d22fa5c 100644 --- a/packages/component-aws-ses/src/EmailBackend.js +++ b/packages/component-aws-ses/src/EmailBackend.js @@ -1,5 +1,4 @@ const nodemailer = require('nodemailer') -const bodyParser = require('body-parser') const AWS = require('aws-sdk') const config = require('config') const _ = require('lodash') @@ -7,36 +6,21 @@ const logger = require('@pubsweet/logger') const sesConfig = _.get(config, 'pubsweet-component-aws-ses') -const EmailBackend = app => { - app.use(bodyParser.json()) - const authBearer = app.locals.passport.authenticate('bearer', { - session: false, - }) - app.post('/api/email', authBearer, async (req, res) => { +module.exports = { + sendEmail: (toEmail, subject, textBody, htmlBody) => { AWS.config.update({ secretAccessKey: sesConfig.secretAccessKey, accessKeyId: sesConfig.accessKeyId, region: sesConfig.region, }) - const { email, subject, textBody, htmlBody } = req.body - if ( - email === undefined || - subject === undefined || - textBody === undefined || - htmlBody === undefined - ) { - res.status(400).json({ error: 'all parameters are required' }) - logger.error('some parameters are missing') - return - } const transporter = nodemailer.createTransport({ SES: new AWS.SES(), }) transporter.sendMail( { from: sesConfig.sender, - to: email, + to: toEmail, subject, text: textBody, html: htmlBody, @@ -48,8 +32,5 @@ const EmailBackend = app => { logger.debug(info) }, ) - res.status(204).json() - }) + }, } - -module.exports = EmailBackend diff --git a/packages/component-mail-service/.gitignore b/packages/component-mail-service/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3614a810088d89d9ccaa28d82401545634874a18 --- /dev/null +++ b/packages/component-mail-service/.gitignore @@ -0,0 +1,8 @@ +_build/ +api/ +logs/ +node_modules/ +uploads/ +.env.* +.env +config/local*.* \ No newline at end of file diff --git a/packages/component-mail-service/README.md b/packages/component-mail-service/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/component-mail-service/index.js b/packages/component-mail-service/index.js new file mode 100644 index 0000000000000000000000000000000000000000..3d0b3c14535e01c151f895accdfd5e0ccfc1294b --- /dev/null +++ b/packages/component-mail-service/index.js @@ -0,0 +1 @@ +module.exports = require('./src/Mail') diff --git a/packages/component-mail-service/package.json b/packages/component-mail-service/package.json new file mode 100644 index 0000000000000000000000000000000000000000..09ca4ea72eafe7a7507fef3b6cd682216dad9850 --- /dev/null +++ b/packages/component-mail-service/package.json @@ -0,0 +1,30 @@ +{ + "name": "pubsweet-component-mail-service", + "version": "0.0.1", + "description": "mail service component for pubsweet", + "license": "MIT", + "files": [ + "src" + ], + "scripts": { + "test": "jest" + }, + "repository": { + "type": "git", + "url": "https://gitlab.coko.foundation/xpub/xpub" + }, + "dependencies": { + "body-parser": "^1.17.2", + "handlebars": "^4.0.11" + }, + "peerDependencies": { + "@pubsweet/logger": "^0.0.1", + "pubsweet": "^1.1.1", + "pubsweet-client": "^1.1.1", + "pubsweet-server": "^1.0.1" + }, + "devDependencies": { + "jest": "^22.1.1", + "supertest": "^3.0.0" + } +} diff --git a/packages/component-mail-service/src/Mail.js b/packages/component-mail-service/src/Mail.js new file mode 100644 index 0000000000000000000000000000000000000000..23644ec833d62036e394e8f933810fad077ae486 --- /dev/null +++ b/packages/component-mail-service/src/Mail.js @@ -0,0 +1,50 @@ +const fs = require('fs') +const handlebars = require('handlebars') +const querystring = require('querystring') +const SES = require('pubsweet-components-aws-ses') +const config = require('config') + +const resetUrl = config.get('admin-reset-password.url') + +module.exports = { + setupEmail: async (email, emailType, token, comment = '') => { + let subject + const htmlFile = readFile(`${__dirname}/templates/${emailType}.html`) + const textFile = readFile(`${__dirname}/templates/${emailType}.txt`) + let replacements = {} + const htmlTemplate = handlebars.compile(htmlFile) + const textTemplate = handlebars.compile(textFile) + + switch (emailType) { + case 'invite-editor-in-chief': + subject = 'Hindawi Invitation' + replacements = { + url: `${resetUrl}?${querystring.encode({ + email, + token, + })}`, + } + break + default: + subject = 'Welcome to Hindawi!' + break + } + + const htmlBody = htmlTemplate(replacements) + const textBody = textTemplate(replacements) + + SES.sendEmail(email, subject, textBody, htmlBody) + }, +} + +const readFile = path => { + const file = fs.readFileSync(path, { encoding: 'utf-8' }, (err, file) => { + if (err) { + throw err + } else { + return file + } + }) + + return file +} diff --git a/packages/component-mail-service/src/templates/invite-editor-in-chief.html b/packages/component-mail-service/src/templates/invite-editor-in-chief.html new file mode 100644 index 0000000000000000000000000000000000000000..58abc88115fe94ea3601b861c1703316e49f0446 --- /dev/null +++ b/packages/component-mail-service/src/templates/invite-editor-in-chief.html @@ -0,0 +1,2 @@ +<p>You have been invited</p> +<p>Here is your url <a href="{{ url }}">PW RESET</a></p> \ No newline at end of file diff --git a/packages/component-mail-service/src/templates/invite-editor-in-chief.txt b/packages/component-mail-service/src/templates/invite-editor-in-chief.txt new file mode 100644 index 0000000000000000000000000000000000000000..d249e30cc14554c2a87ba079439bbba7d2b2d976 --- /dev/null +++ b/packages/component-mail-service/src/templates/invite-editor-in-chief.txt @@ -0,0 +1,2 @@ +You have been invited +Here is your url PW REST {{ url }} \ No newline at end of file diff --git a/packages/components-faraday/src/components/Admin/Admin.js b/packages/components-faraday/src/components/Admin/Admin.js index 10a21ca47871a8863218d5d62faa879851571543..929cda6e26fdd300baf94bc5b0001ebcbe527efc 100644 --- a/packages/components-faraday/src/components/Admin/Admin.js +++ b/packages/components-faraday/src/components/Admin/Admin.js @@ -1,17 +1,196 @@ import React from 'react' import styled from 'styled-components' +import { Icon, Menu } from '@pubsweet/ui' -const Admin = ({ users = [] }) => ( - <Root> - <h2>Admin</h2> - <ul> - {users.map((u, i) => ( - <li key={u.id}> - {u.username} - {u.email} - </li> - ))} - </ul> - </Root> +import { Pagination } from './' + +const AddButton = styled.button` + align-items: center; + border: none; + cursor: pointer; + display: flex; + font-family: Helvetica; + font-size: 12px; + text-align: left; + color: #667080; + + &:active, + &:focus { + outline: none; + } +` + +const Header = styled.div` + display: flex; + flex-direction: row; + align-items: center; + + & span { + font-family: Helvetica; + font-size: 24px; + font-weight: bold; + text-align: left; + color: #667080; + } +` + +const SubHeader = styled.div` + align-items: center; + border-bottom: 1px solid #667080; + display: flex; + flex-direction: row; + justify-content: space-between; + margin-top: 20px; + padding-bottom: 10px; + + > div:first-child { + display: flex; + align-items: center; + } + + span { + font-family: Helvetica; + font-size: 14px; + text-align: left; + color: #667080; + } +` + +const Table = styled.table` + border-spacing: 0; + border-collapse: collapse; + margin-top: 10px; + width: 100vw; + + & thead tr { + height: 40px; + border-bottom: 1px solid #667080; + font-family: Helvetica; + font-size: 14px; + font-weight: bold; + text-align: left; + color: #667080; + } +` + +const Row = styled.tr` + border-bottom: 1px solid #667080; + color: #667080; + font-family: Helvetica; + font-size: 14px; + height: 40px; + text-align: left; +` + +const Status = styled.td` + & span { + border: solid 1px #667080; + text-transform: uppercase; + font-family: Helvetica; + font-size: 12px; + font-weight: bold; + text-align: left; + color: #667080; + padding: 2px 10px; + } +` + +const Input = styled.input` + height: 20px; + width: 20px; +` + +const TableRow = ({ toggleUser, selected, email, username, type }) => ( + <Row> + <td> + <Input checked={selected} onClick={toggleUser} type="checkbox" /> + </td> + <td>{email}</td> + <td>{username}</td> + <td>affiliation here</td> + <td>country here</td> + <td>{type}</td> + <Status> + <span>status</span> + </Status> + </Row> +) + +const Admin = ({ + users, + toggleUser, + toggleAllUsers, + incrementPage, + decrementPage, + page, + itemsPerPage, +}) => ( + <div> + <Header> + <span>Users</span> + <AddButton> + <Icon color="#667080">plus-circle</Icon> + Add User + </AddButton> + </Header> + <SubHeader> + <div> + <span>Bulk actions: </span> + <Menu + onChange={value => value} + options={[ + { value: 'deactivate', label: 'Deactivate' }, + { value: 'activate', label: 'Activate' }, + ]} + value="activate" + /> + + <Menu + onChange={value => value} + options={[ + { value: 'sort', label: 'SORT' }, + { value: 'unsort', label: 'UNSORT' }, + ]} + value="sort" + /> + + <Icon color="#667080" size={24}> + search + </Icon> + </div> + <Pagination + decrementPage={decrementPage} + incrementPage={incrementPage} + itemsPerPage={itemsPerPage} + page={page} + /> + </SubHeader> + + <Table> + <thead> + <tr> + <td> + <Input + checked={users.every(u => u.selected)} + onClick={toggleAllUsers} + type="checkbox" + /> + </td> + <td>Email</td> + <td>Full name</td> + <td>Affiliation</td> + <td>Country</td> + <td>Roles</td> + <td>Status</td> + </tr> + </thead> + <tbody> + {users.map(u => ( + <TableRow key={u.id} {...u} toggleUser={toggleUser(u)} /> + ))} + </tbody> + </Table> + </div> ) export default Admin diff --git a/packages/components-faraday/src/components/Admin/AdminPage.js b/packages/components-faraday/src/components/Admin/AdminPage.js index 684f7df5ddd552914641e4961163e89fbc134b9e..075f484484a7e72b8659a3ef1b9138a30b381390 100644 --- a/packages/components-faraday/src/components/Admin/AdminPage.js +++ b/packages/components-faraday/src/components/Admin/AdminPage.js @@ -4,11 +4,33 @@ import { connect } from 'react-redux' import { actions } from 'pubsweet-client' import { ConnectPage } from 'xpub-connect' import { withRouter } from 'react-router-dom' +import { compose, withState, withHandlers } from 'recompose' import Admin from './Admin' export default compose( ConnectPage(() => [actions.getUsers()]), withRouter, - connect(state => ({ users: get(state, 'users.users') })), + connect(state => ({ currentUsers: get(state, 'users.users') })), + withState('users', 'setUsers', props => + props.currentUsers.map(u => ({ ...u, selected: false })), + ), + withState('itemsPerPage', 'setItemsPerPage', 50), + withState('page', 'setPage', 0), + withHandlers({ + incrementPage: ({ setPage }) => () => { + setPage(p => p + 1) + }, + decrementPage: ({ setPage }) => () => { + setPage(p => (p > 0 ? p - 1 : p)) + }, + toggleUser: ({ setUsers }) => user => () => { + setUsers(prev => + prev.map(u => (u.id === user.id ? { ...u, selected: !u.selected } : u)), + ) + }, + toggleAllUsers: ({ setUsers }) => () => { + setUsers(users => users.map(u => ({ ...u, selected: !u.selected }))) + }, + }), )(Admin) diff --git a/packages/components-faraday/src/components/Admin/Pagination.js b/packages/components-faraday/src/components/Admin/Pagination.js new file mode 100644 index 0000000000000000000000000000000000000000..df4373a0d4f4fe2e93df637c90e8664c4060bde9 --- /dev/null +++ b/packages/components-faraday/src/components/Admin/Pagination.js @@ -0,0 +1,70 @@ +import React from 'react' +import styled from 'styled-components' +import { Icon } from '@pubsweet/ui' + +const Root = styled.div` + display: flex; + align-items: center; +` + +const Chevrons = styled.div` + display: flex; + align-items: center; +` + +const IconButton = styled.button` + align-items: center; + border: none; + cursor: pointer; + display: flex; + font-family: Helvetica; + font-size: 12px; + text-align: left; + color: #667080; + + &:active, + &:focus { + outline: none; + } +` + +const Showing = styled.div` + display: flex; + align-items: center; + + span:first-child { + font-family: Helvetica; + font-size: 14px; + text-align: left; + color: #667080; + margin-right: 10px; + } + span:last-child { + border: solid 1px #667080; + padding: 2px 10px; + } +` + +const Pagination = ({ page, itemsPerPage, incrementPage, decrementPage }) => ( + <Root> + <Showing> + <span>Showing:</span> + <span>50</span> + </Showing> + <Chevrons> + <IconButton onClick={decrementPage}> + <Icon color="#667080" size={18}> + chevron-left + </Icon> + </IconButton> + <span>{`${page * itemsPerPage + 1} to ${page * itemsPerPage + 50}`}</span> + <IconButton onClick={incrementPage}> + <Icon color="#667080" size={18}> + chevron-right + </Icon> + </IconButton> + </Chevrons> + </Root> +) + +export default Pagination diff --git a/packages/components-faraday/src/components/Admin/index.js b/packages/components-faraday/src/components/Admin/index.js index f623059ef006ec5d6c516c651bdaa474feb4f073..4f1e2831e6e6710d184684b30b4257f9493ea47a 100644 --- a/packages/components-faraday/src/components/Admin/index.js +++ b/packages/components-faraday/src/components/Admin/index.js @@ -1,3 +1,4 @@ import AdminPage from './AdminPage' export default AdminPage +export { default as Pagination } from './Pagination' diff --git a/packages/components-faraday/src/components/Dashboard/Dashboard.js b/packages/components-faraday/src/components/Dashboard/Dashboard.js index 29ba564fd069e3d25afe4247899b5f5917afd15f..110f39981fe79f44e7671d3c02fec67ebd76e666 100644 --- a/packages/components-faraday/src/components/Dashboard/Dashboard.js +++ b/packages/components-faraday/src/components/Dashboard/Dashboard.js @@ -23,14 +23,24 @@ const Dashboard = ({ filterItems, abstractModal, setModal, + history, ...rest }) => ( <div className={classes.root}> <div className={classes.header}> <div className={classes.heading}>Manuscripts</div> - <Button onClick={createDraftSubmission} primary> - New - </Button> + <div className={classes.headerButtons}> + <Button + className={classes['admin-button']} + onClick={() => history.push('admin')} + primary + > + Admin dashboard + </Button> + <Button onClick={createDraftSubmission} primary> + New + </Button> + </div> </div> <DashboardFilters changeFilterValue={changeFilterValue} diff --git a/packages/components-faraday/src/components/Dashboard/Dashboard.local.scss b/packages/components-faraday/src/components/Dashboard/Dashboard.local.scss index 5a2dc6819b679b6e45fd78737c23c00a77787a86..b4fb082487cd95ba901f0c2dcfb120d20b300312 100644 --- a/packages/components-faraday/src/components/Dashboard/Dashboard.local.scss +++ b/packages/components-faraday/src/components/Dashboard/Dashboard.local.scss @@ -67,6 +67,14 @@ justify-content: space-between; } +.headerButtons { + display: flex; + + .admin-button { + margin-right: 15px; + } +} + .heading { color: var(--color-primary); font-size: 1.6em; diff --git a/packages/xpub-faraday/config/components.json b/packages/xpub-faraday/config/components.json index 8c34645a85924f65fc2e2d8475db8b2d05cba90c..d64253bf25516eebd779a3f72a6f728e37ef63c6 100644 --- a/packages/xpub-faraday/config/components.json +++ b/packages/xpub-faraday/config/components.json @@ -7,5 +7,5 @@ "pubsweet-component-wizard", "pubsweet-components-faraday", "pubsweet-components-aws-s3", - "pubsweet-components-aws-ses" + "pubsweet-component-admin" ] diff --git a/packages/xpub-faraday/config/default.js b/packages/xpub-faraday/config/default.js index d41444fcc32124eeb24639c80380e9f2536f3204..245f1dbe7b3d34ab7c80d7399c2d000240341f51 100644 --- a/packages/xpub-faraday/config/default.js +++ b/packages/xpub-faraday/config/default.js @@ -25,7 +25,7 @@ module.exports = { 'pubsweet-client': { API_ENDPOINT: '/api', 'login-redirect': '/', - 'redux-log': false, + 'redux-log': true, theme: process.env.PUBSWEET_THEME, }, 'mail-transport': { @@ -60,6 +60,11 @@ module.exports = { region: process.env.AWS_SES_REGION, sender: process.env.EMAIL_SENDER, }, + 'admin-reset-password': { + url: + process.env.PUBSWEET_ADMIN_PASSWORD_RESET_URL || + 'http://localhost:3000/admin/password-reset', + }, publicKeys: [ 'pubsweet-client', 'authsome', diff --git a/packages/xpub-faraday/config/validations.js b/packages/xpub-faraday/config/validations.js index f7d617d3d72c0ead1b9bc4390c10a45445006929..8b98883df175275ecb57a268cf4f549d065a92c3 100644 --- a/packages/xpub-faraday/config/validations.js +++ b/packages/xpub-faraday/config/validations.js @@ -88,6 +88,13 @@ module.exports = { user: { name: Joi.string(), // TODO: add "name" to the login form roles: Joi.object(), + isConfirmed: Joi.boolean(), + firstName: Joi.string().allow(''), + lastName: Joi.string().allow(''), + middleName: Joi.string().allow(''), + affiliation: Joi.string().allow(''), + position: Joi.string().allow(''), + title: Joi.string().allow(''), }, team: { group: Joi.string(), diff --git a/packages/xpub-faraday/package.json b/packages/xpub-faraday/package.json index fabc887cc97882eb924de656b768f103fbf52326..e3b80d41b71900568e6bb0eb5d821f06df2765f7 100644 --- a/packages/xpub-faraday/package.json +++ b/packages/xpub-faraday/package.json @@ -31,7 +31,7 @@ "pubsweet-component-xpub-submit": "^0.0.2", "pubsweet-server": "1.0.5", "pubsweet-components-aws-s3": "^0.0.1", - "pubsweet-components-aws-ses": "^0.0.1", + "pubsweet-component-admin": "^0.0.1", "react": "^15.6.1", "react-dnd": "^2.5.4", "react-dnd-html5-backend": "^2.5.4", diff --git a/yarn.lock b/yarn.lock index eaeb4eea2fab94726a5a42f59a381f52f3ad1025..76654745dcb4355df780499d9a0f89f31cf8640e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4324,7 +4324,7 @@ handle-thing@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" -handlebars@^4.0.2, handlebars@^4.0.3: +handlebars@^4.0.11, handlebars@^4.0.2, handlebars@^4.0.3: version "4.0.11" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" dependencies: