diff --git a/app/components/RolesUpdater.js b/app/components/RolesUpdater.js
index ea6240230c09859a1fe27718aac91bc3f7ece0e5..8a20cb733e6a82af102a7b256fa7b915a0ecb7f9 100644
--- a/app/components/RolesUpdater.js
+++ b/app/components/RolesUpdater.js
@@ -1,7 +1,7 @@
-import { useCallback } from 'react'
+import React, { useCallback } from 'react'
 import { useQuery } from '@apollo/client'
+import { Redirect } from 'react-router-dom'
 import { GET_CURRENT_USER } from '../queries'
-
 import currentRolesVar from '../shared/currentRolesVar'
 
 const updateStuff = data => {
@@ -12,13 +12,18 @@ const updateStuff = data => {
 
 const RolesUpdater = ({ children, history, match }) => {
   // This updates the current roles app-wide using Apollo's makeVar
-  useQuery(GET_CURRENT_USER, {
+  const { error } = useQuery(GET_CURRENT_USER, {
     pollInterval: 5000,
     notifyOnNetworkStatusChange: true,
     fetchPolicy: 'network-only',
     // TODO: useCallback used because of bug: https://github.com/apollographql/apollo-client/issues/6301
     onCompleted: useCallback(data => updateStuff(data), []),
   })
+
+  if (error && !error.networkError) {
+    return <Redirect to="/login" />
+  }
+
   return null
 }
 
diff --git a/app/components/component-formbuilder/src/components/ComponentProperties.jsx b/app/components/component-formbuilder/src/components/ComponentProperties.jsx
index ff375d341d4651690209e96dc3f4e4985c11c232..5c079e05da5ddab7f11dcbbf45fd9a00e77b6629 100644
--- a/app/components/component-formbuilder/src/components/ComponentProperties.jsx
+++ b/app/components/component-formbuilder/src/components/ComponentProperties.jsx
@@ -1,21 +1,20 @@
-import React from 'react'
+import React, { useState } from 'react'
 import { map, omitBy } from 'lodash'
-import {
-  branch,
-  renderComponent,
-  compose,
-  withState,
-  withHandlers,
-  withProps,
-} from 'recompose'
+// import {
+//   branch,
+//   renderComponent,
+//   compose,
+//   withState,
+//   withHandlers,
+//   withProps,
+// } from 'recompose'
 import { ValidatedFieldFormik, Menu, Button } from '@pubsweet/ui'
-import { withFormik } from 'formik'
+import { Formik } from 'formik'
 
 import FormProperties from './FormProperties'
 import components from './config/Elements'
 import * as elements from './builderComponents'
-import { Page, Heading } from './molecules/Page'
-import { Section, Legend } from './styles'
+import { Section, Legend, Page, Heading } from './style'
 
 const MenuComponents = input => (
   <Menu
@@ -29,8 +28,8 @@ const MenuComponents = input => (
 
 const ComponentProperties = ({
   properties,
-  changeComponent,
-  selectComponentValue,
+  setComponent,
+  selectedComponent,
   handleSubmit,
   setFieldValue,
 }) => (
@@ -43,18 +42,18 @@ const ComponentProperties = ({
           component={MenuComponents}
           name="component"
           onChange={value => {
-            changeComponent(value)
+            setComponent(value)
             setFieldValue('component', value)
           }}
         />
       </Section>
-      {selectComponentValue &&
-        map(components[selectComponentValue], (value, key) => (
+      {selectedComponent &&
+        map(components[selectedComponent], (value, key) => (
           <Section key={key}>
             <Legend space>{`Field ${key}`}</Legend>
             <ValidatedFieldFormik
               component={elements[value.component].default}
-              key={`${selectComponentValue}-${key}`}
+              key={`${selectedComponent}-${key}`}
               name={key}
               onChange={event => {
                 let value = {}
@@ -77,45 +76,92 @@ const ComponentProperties = ({
   </Page>
 )
 
-const UpdateForm = ({ onSubmitFn, properties, changeTabs }) => (
+const UpdateForm = ({ handleSubmit, properties, setFieldValue }) => (
   <FormProperties
+    handleSubmit={handleSubmit}
     mode="update"
-    onSubmitFn={onSubmitFn}
     properties={properties}
+    setFieldValue={setFieldValue}
   />
 )
 
-const onSubmit = (values, { onSubmitFn, properties }) => {
-  if (!values.id || !values.component) return
-
-  const children = omitBy(values, value => value === '')
-  onSubmitFn({ id: properties.id }, Object.assign({}, { children }))
+const prepareForSubmit = values => {
+  const cleanedValues = omitBy(values, value => value === '')
+  return JSON.stringify(cleanedValues)
 }
 
-const ComponentForm = compose(
-  withProps(({ properties }) => ({
-    initialValues: { children: properties.properties },
-  })),
-  withFormik({
-    displayName: 'ComponentSubmit',
-    mapPropsToValues: data => data.properties.properties,
-    handleSubmit: (props, { props: { onSubmitFn, id, properties } }) =>
-      onSubmit(props, { onSubmitFn, properties }),
-  }),
-  withState(
-    'selectComponentValue',
-    'selectComponent',
-    ({ properties }) => properties.properties.component,
-  ),
-  withHandlers({
-    changeComponent: ({ selectComponent }) => component =>
-      selectComponent(() => component),
-  }),
-)(ComponentProperties)
+// const ComponentForm = compose(
+// withProps(({ properties }) => ({
+//   initialValues: { children: properties.properties },
+// })),
+// withFormik({
+//   displayName: 'ComponentSubmit',
+//   mapPropsToValues: data => data.properties.properties,
+//   handleSubmit: (props, { props: { onSubmitFn, id, properties } }) =>
+//     onSubmit(props, { onSubmitFn, properties }),
+// }),
+// withState(
+//   'selectComponentValue',
+//   'selectComponent',
+//   ({ properties }) => properties.properties.component,
+// ),
+// withHandlers({
+//   changeComponent: ({ selectComponent }) => component =>
+//     selectComponent(() => component),
+// }),
+// )(ComponentProperties)
 
-export default compose(
-  branch(
-    ({ properties }) => properties.type === 'form',
-    renderComponent(UpdateForm),
-  )(ComponentForm),
-)
+// export default compose(
+//   branch(
+//     ({ properties }) => properties.type === 'form',
+//     renderComponent(UpdateForm),
+//   )(ComponentForm),
+// )
+
+const ComponentForm = ({ updateForm, updateFormElement, ...props }) => {
+  const [selectedComponent, setComponent] = useState(
+    props.properties.properties.component,
+  )
+  return props.properties.type === 'form' ? (
+    <Formik
+      initialValues={props.properties.properties}
+      onSubmit={values =>
+        updateForm({
+          variables: { formId: values.id, form: prepareForSubmit(values) },
+        })
+      }
+    >
+      {formikProps => (
+        <UpdateForm
+          handleSubmit={formikProps.handleSubmit}
+          properties={props.properties}
+          setFieldValue={formikProps.setFieldValue}
+        />
+      )}
+    </Formik>
+  ) : (
+    <Formik
+      initialValues={props.properties.properties}
+      onSubmit={values =>
+        updateFormElement({
+          variables: {
+            formId: props.properties.formId,
+            element: prepareForSubmit(values),
+          },
+        })
+      }
+    >
+      {formikProps => (
+        <ComponentProperties
+          displayName="ComponentSubmit"
+          handleSubmit={formikProps.handleSubmit}
+          selectedComponent={selectedComponent}
+          setComponent={setComponent}
+          setFieldValue={formikProps.setFieldValue}
+        />
+      )}
+    </Formik>
+  )
+}
+
+export default ComponentForm
diff --git a/app/components/component-formbuilder/src/components/FormBuilder.jsx b/app/components/component-formbuilder/src/components/FormBuilder.jsx
index b8b6d20202dd47ec8ef3efdb298560e44750ee20..1cd13d08431763cae0a3716da068426b99b42c56 100644
--- a/app/components/component-formbuilder/src/components/FormBuilder.jsx
+++ b/app/components/component-formbuilder/src/components/FormBuilder.jsx
@@ -1,15 +1,16 @@
 import React, { useState } from 'react'
 import styled, { withTheme } from 'styled-components'
 import { unescape } from 'lodash'
-import { th } from '@pubsweet/ui-toolkit'
+import { th, grid } from '@pubsweet/ui-toolkit'
 import { Icon, Action } from '@pubsweet/ui'
-import { Page } from './molecules/Page'
+import { Page, Heading } from './style'
 
 const Element = styled.div`
   display: flex;
-  border: 1px solid #000;
-  padding: 10px;
-  margin: 10px;
+  border: 1px solid ${th('colorBorder')};
+  padding: ${grid(2)} ${grid(1)};
+  border-radius: ${th('borderRadius')};
+  margin: ${grid(2)};
   justify-content: space-between;
 `
 
@@ -51,38 +52,40 @@ const Main = styled.div`
   justify-content: center;
 `
 
-const Info = styled.div`
-  color: ${th('colorPrimary')};
-  font-size: 2em;
-  font-weight: 400;
-  text-transform: uppercase;
-  display: inline-flex;
-  padding: calc(8px / 2);
-`
-
 const ElementTitle = styled.span``
 
 const createMarkup = encodedHtml => ({
   __html: unescape(encodedHtml),
 })
 
-const BuilderElement = ({ elements, changeProperties, deleteElement, form }) =>
-  elements.map((value, key) => (
-    <Element key={`element-${value.id}`}>
-      <Action
-        onClick={() =>
-          changeProperties({
-            type: 'element',
-            properties: value,
-          })
-        }
-      >
-        <ElementTitle dangerouslySetInnerHTML={createMarkup(value.title)} /> (
-        {value.component})
-      </Action>
-      <Action onClick={() => deleteElement(form.id, value.id)}>x</Action>
-    </Element>
-  ))
+const BuilderElement = ({ elements, setProperties, deleteFormElement, form }) =>
+  [...elements]
+    .sort((obj1, obj2) => parseInt(obj1.order, 10) - parseInt(obj2.order, 10))
+    .map((value, key) => (
+      <Element key={`element-${value.id}`}>
+        <Action
+          onClick={() =>
+            setProperties({
+              type: 'element',
+              formId: form.id,
+              properties: value,
+            })
+          }
+        >
+          <ElementTitle dangerouslySetInnerHTML={createMarkup(value.title)} /> (
+          {value.component})
+        </Action>
+        <Action
+          onClick={() =>
+            deleteFormElement({
+              variables: { formId: form.id, elementId: value.id },
+            })
+          }
+        >
+          x
+        </Action>
+      </Element>
+    ))
 
 const AddButtonElement = ({ addElement }) => (
   <Root>
@@ -95,8 +98,10 @@ const AddButtonElement = ({ addElement }) => (
           })
         }
       >
-        <StatusIdle />
-        <Info>Add Element</Info>
+        <Heading>
+          <StatusIdle />
+          Add Element
+        </Heading>
       </Action>
     </Main>
   </Root>
@@ -106,8 +111,8 @@ const FormBuilder = ({
   form,
   // elements,
   // addElements,
-  changeProperties,
-  deleteElement,
+  setProperties,
+  deleteFormElement,
 }) => {
   const [elements, setElements] = useState(form.children || [])
   const addElement = element => {
@@ -122,11 +127,11 @@ const FormBuilder = ({
       <AddButtonElement addElement={addElement} form={form} id="add-element" />
       {elements && elements.length > 0 && (
         <BuilderElement
-          changeProperties={changeProperties}
-          deleteElement={deleteElement}
+          deleteFormElement={deleteFormElement}
           elements={elements}
           form={form}
           id="builder-element"
+          setProperties={setProperties}
         />
       )}
     </Page>
diff --git a/app/components/component-formbuilder/src/components/FormBuilderLayout.jsx b/app/components/component-formbuilder/src/components/FormBuilderLayout.jsx
index 350ef84cfcf8c4073995367cab7b98770fb6a779..0e2e5a4e818d6b96ae779eb7c49f758abb3b22ac 100644
--- a/app/components/component-formbuilder/src/components/FormBuilderLayout.jsx
+++ b/app/components/component-formbuilder/src/components/FormBuilderLayout.jsx
@@ -2,43 +2,49 @@ import React from 'react'
 import { forEach } from 'lodash'
 import styled from 'styled-components'
 import { Tabs, Action } from '@pubsweet/ui'
-import { Columns, Details, Form } from './atoms/Columns'
+import { Columns, Details, Form } from './style'
 import ComponentProperties from './ComponentProperties'
 import FormBuilder from './FormBuilder'
 import FormProperties from './FormProperties'
+import {
+  Container,
+  HeadingWithAction,
+  Heading,
+  SectionContent,
+  SectionRow,
+} from '../../../shared'
 
 const DeleteIcon = styled(Action)`
   margin-left: 10px;
   line-height: 1.15;
 `
 
-const DetailsStyled = styled(Details)`
-  border-left: 1px solid black;
-  padding-left: 40px;
-`
-
 const FormBuilderLayout = ({
   getForms,
   properties,
   deleteForm,
-  deleteElement,
+  deleteFormElement,
   updateForm,
   createForm,
-  updateElements,
-  changeProperties,
-  changeTabs,
+  updateFormElement,
+  setProperties,
+  setActiveTab,
   activeTab,
 }) => {
   const Sections = []
   forEach(getForms, (form, key) => {
     Sections.push({
       content: (
-        <FormBuilder
-          changeProperties={changeProperties}
-          deleteElement={deleteElement}
-          form={form}
-          key={`form-builder-${key}`}
-        />
+        <SectionContent>
+          <SectionRow>
+            <FormBuilder
+              deleteFormElement={deleteFormElement}
+              form={form}
+              key={`form-builder-${key}`}
+              setProperties={setProperties}
+            />
+          </SectionRow>
+        </SectionContent>
       ),
       key: `${key}`,
       label: [
@@ -58,41 +64,54 @@ const FormBuilderLayout = ({
 
   Sections.push({
     content: (
-      <FormProperties
-        key="form-builder-new"
-        mode="create"
-        onSubmitFn={createForm}
-        properties={{}}
-      />
+      <SectionContent>
+        <SectionRow>
+          <FormProperties
+            key="form-builder-new"
+            mode="create"
+            onSubmitFn={createForm}
+            properties={{}}
+          />
+        </SectionRow>
+      </SectionContent>
     ),
     key: 'new',
     label: '+ Add Form',
   })
   return (
-    <Columns>
-      <Form>
-        <Tabs
-          activeKey={`${activeTab}`}
-          onChange={tab => {
-            changeProperties({
-              type: 'form',
-              properties: getForms[tab],
-            })
-            changeTabs(tab)
-          }}
-          sections={Sections}
-          title="builder"
-        />
-      </Form>
-      <DetailsStyled>
-        <ComponentProperties
-          changeTabs={changeTabs}
-          key={`${properties.type}-${(properties.properties || {}).id}`}
-          onSubmitFn={properties.type === 'form' ? updateForm : updateElements}
-          properties={properties}
-        />
-      </DetailsStyled>
-    </Columns>
+    <Container>
+      <HeadingWithAction>
+        <Heading>Form Builder</Heading>
+      </HeadingWithAction>
+      <Columns>
+        <Form>
+          <Tabs
+            activeKey={`${activeTab}`}
+            onChange={tab => {
+              setProperties({
+                type: 'form',
+                properties: getForms[tab],
+              })
+              setActiveTab(tab)
+            }}
+            sections={Sections}
+          />
+        </Form>
+        <Details>
+          <SectionContent>
+            <SectionRow>
+              <ComponentProperties
+                key={`${properties.type}-${(properties.properties || {}).id}`}
+                properties={properties}
+                setActiveTab={setActiveTab}
+                updateForm={updateForm}
+                updateFormElement={updateFormElement}
+              />
+            </SectionRow>
+          </SectionContent>
+        </Details>
+      </Columns>
+    </Container>
   )
 }
 
diff --git a/app/components/component-formbuilder/src/components/FormBuilderLayout.md b/app/components/component-formbuilder/src/components/FormBuilderLayout.md
deleted file mode 100644
index ca3d1efb62d3a48bd517d1a319597203229e15af..0000000000000000000000000000000000000000
--- a/app/components/component-formbuilder/src/components/FormBuilderLayout.md
+++ /dev/null
@@ -1,50 +0,0 @@
-A form builder to create forms
-
-```js
-const forms = [
-  {
-    name: 'test form',
-    id: 'test',
-    children: [
-      {
-        title: 'Title',
-        id: '1531303631370',
-        component: 'AbstractEditor',
-        name: 'metadata.title',
-        placeholder: 'Enter Title...',
-        validate: ['required'],
-        validateValue: { minChars: '10' },
-      },
-      {
-        title: 'Title 1',
-        id: '1531303631371',
-        component: 'AbstractEditor',
-        name: 'metadata.title',
-        placeholder: 'Enter Title...',
-        validate: ['required'],
-        validateValue: { minChars: '10' },
-      },
-    ],
-  },
-  { name: 'test form 1', id: 'test1' },
-]
-
-initialState = {
-  properties: {
-    type: 'form',
-    properties: forms[0],
-  },
-  activeTab: 0,
-}
-;<div style={{ position: 'relative', height: '100%' }}>
-  <FormBuilderLayout
-    getForms={forms}
-    activeTab={state.activeTab}
-    properties={state.properties}
-    changeProperties={value => {
-      setState({ properties: value })
-    }}
-    changeTabs={value => setState({ activeTab: value })}
-  />
-</div>
-```
diff --git a/app/components/component-formbuilder/src/components/FormBuilderPage.js b/app/components/component-formbuilder/src/components/FormBuilderPage.js
index 9cfb45edbf88a31a577329ef35ea428ac5dfa572..17b795534b8ab540cc3808a79cbd9c7d00d3af0b 100644
--- a/app/components/component-formbuilder/src/components/FormBuilderPage.js
+++ b/app/components/component-formbuilder/src/components/FormBuilderPage.js
@@ -1,35 +1,37 @@
-import { compose, withState, withHandlers, withProps } from 'recompose'
-import { graphql } from '@apollo/client/react/hoc'
-import gql from 'graphql-tag'
-import { withLoader } from 'pubsweet-client'
+import React, { useState, useEffect } from 'react'
+// import { compose, withState, withHandlers, withProps } from 'recompose'
+import { useQuery, useMutation, gql } from '@apollo/client'
+// import gql from 'graphql-tag'
+// import { withLoader } from 'pubsweet-client'
 
 import FormBuilderLayout from './FormBuilderLayout'
+import { Spinner } from '../../../shared'
 
-const createForm = gql`
+const createFormMutation = gql`
   mutation($form: String!) {
     createForm(form: $form)
   }
 `
 
-const updateForm = gql`
+const updateFormMutation = gql`
   mutation($form: String!, $id: String!) {
     updateForm(form: $form, id: $id)
   }
 `
 
-const updateFormElements = gql`
-  mutation($form: String!, $formId: String!) {
-    updateFormElements(form: $form, formId: $formId)
+const updateFormElementMutation = gql`
+  mutation($element: String!, $formId: String!) {
+    updateFormElement(element: $element, formId: $formId)
   }
 `
 
-const deleteFormElement = gql`
+const deleteFormElementMutation = gql`
   mutation($formId: ID!, $elementId: ID!) {
     deleteFormElement(formId: $formId, elementId: $elementId)
   }
 `
 
-const deleteForms = gql`
+const deleteFormMutation = gql`
   mutation($formId: ID!) {
     deleteForms(formId: $formId)
   }
@@ -47,80 +49,125 @@ const query = gql`
   }
 `
 
-export default compose(
-  graphql(query),
-  graphql(deleteForms, {
-    name: 'deleteForms',
-  }),
-  graphql(deleteFormElement, {
-    name: 'deleteFormElement',
-  }),
-  graphql(updateForm, {
-    name: 'updateForm',
-  }),
-  graphql(createForm, {
-    name: 'createForm',
-  }),
-  graphql(updateFormElements, {
-    name: 'updateFormElements',
-  }),
-  withLoader(),
-  withProps(props => ({
-    deleteForm: formId =>
-      props.deleteForms({
-        variables: {
-          formId,
-        },
-      }),
-    deleteElement: (formId, elementId) =>
-      props.deleteFormElement({
-        variables: {
-          formId,
-          elementId,
-        },
-      }),
-    updateForm: (form, formProperties) =>
-      props.updateForm({
-        variables: {
-          form: JSON.stringify(formProperties),
-          id: form.id,
-        },
-      }),
-    createForm: formProperties =>
-      props.createForm({
-        variables: {
-          form: JSON.stringify(formProperties),
-        },
-      }),
-    updateElements: (form, formElements) =>
-      props.updateFormElements({
-        variables: {
-          form: JSON.stringify(formElements),
-          formId: form.id,
-        },
-      }),
-  })),
-  withState('properties', 'onChangeProperties', ({ getForms }) => ({
-    type: 'form',
-    properties: getForms[0] || {},
-  })),
-  withState('activeTab', 'onChangeTab', ({ getForms, activeTab }) =>
-    getForms.length === 0 ? 'new' : 0,
-  ),
-  withHandlers({
-    changeProperties: ({
-      onChangeProperties,
-      getForms,
-      activeTab,
-    }) => properties =>
-      onChangeProperties(
-        () =>
-          Object.assign({}, properties, {
-            id: (getForms[activeTab] || {}).id,
-          }) || undefined,
-      ),
-    changeTabs: ({ onChangeTab }) => activeTab => {
-      onChangeTab(() => activeTab || '')
-    },
-  }),
-)(FormBuilderLayout)
+const FormBuilderPage = props => {
+  const { loading, data, error } = useQuery(query)
+
+  const [deleteForm] = useMutation(deleteFormMutation)
+  const [deleteFormElement] = useMutation(deleteFormElementMutation)
+
+  const [updateForm] = useMutation(updateFormMutation)
+  const [updateFormElement] = useMutation(updateFormElementMutation)
+  const [createForm] = useMutation(createFormMutation)
+
+  const [properties, setProperties] = useState({ type: 'form', properties: {} })
+  const [activeTab, setActiveTab] = useState()
+
+  useEffect(() => {
+    if (!loading && data) {
+      if (data.getForms.length) {
+        setActiveTab(0)
+        setProperties({ type: 'form', properties: data.getForms[0] })
+      } else {
+        setActiveTab('new')
+      }
+    }
+  }, [loading, data])
+
+  if (loading || activeTab === undefined) return <Spinner />
+  if (error) return JSON.stringify(error)
+
+  return (
+    <FormBuilderLayout
+      activeTab={activeTab}
+      createForm={createForm}
+      deleteForm={deleteForm}
+      deleteFormElement={deleteFormElement}
+      getForms={data.getForms}
+      properties={properties}
+      setActiveTab={setActiveTab}
+      setProperties={setProperties}
+      updateForm={updateForm}
+      updateFormElement={updateFormElement}
+    />
+  )
+}
+
+export default FormBuilderPage
+
+// export default compose(
+// graphql(query),
+// graphql(deleteForms, {
+//   name: 'deleteForms',
+// }),
+// graphql(deleteFormElement, {
+//   name: 'deleteFormElement',
+// }),
+// graphql(updateForm, {
+//   name: 'updateForm',
+// }),
+// graphql(createForm, {
+//   name: 'createForm',
+// }),
+// graphql(updateFormElements, {
+//   name: 'updateFormElements',
+// }),
+// withLoader(),
+// withProps(props => ({
+//   deleteForm: formId =>
+//     props.deleteForms({
+//       variables: {
+//         formId,
+//       },
+//     }),
+//   deleteElement: (formId, elementId) =>
+//     props.deleteFormElement({
+//       variables: {
+//         formId,
+//         elementId,
+//       },
+//     }),
+//   updateForm: (form, formProperties) =>
+//     props.updateForm({
+//       variables: {
+//         form: JSON.stringify(formProperties),
+//         id: form.id,
+//       },
+//     }),
+//   createForm: formProperties =>
+//     props.createForm({
+//       variables: {
+//         form: JSON.stringify(formProperties),
+//       },
+//     }),
+//   updateElements: (form, formElements) =>
+//     props.updateFormElements({
+//       variables: {
+//         form: JSON.stringify(formElements),
+//         formId: form.id,
+//       },
+//     }),
+// })),
+// withState('properties', 'onChangeProperties', ({ getForms }) => ({
+//   type: 'form',
+//   properties: getForms[0] || {},
+// })),
+// withState('activeTab', 'onChangeTab', ({ getForms, activeTab }) =>
+//   getForms.length === 0 ? 'new' : 0,
+// ),
+//   withHandlers({
+//     changeProperties: ({
+//       onChangeProperties,
+//       getForms,
+//       activeTab,
+//     }) => properties =>
+//       onChangeProperties(
+//         () =>
+//           Object.assign({}, properties, {
+//             id: (getForms[activeTab] || {}).id,
+//           }) || undefined,
+//       ),
+//     changeTabs: ({ onChangeTab }) => activeTab => {
+//       onChangeTab(() => activeTab || '')
+//     },
+//   }),
+// )(FormBuilderLayout)
diff --git a/app/components/component-formbuilder/src/components/FormProperties.jsx b/app/components/component-formbuilder/src/components/FormProperties.jsx
index c50d59fd1eab546ff2db9b483c379673d844eff8..5aa9f40f8d31ddfacbd996c21bb9b7093bd45939 100644
--- a/app/components/component-formbuilder/src/components/FormProperties.jsx
+++ b/app/components/component-formbuilder/src/components/FormProperties.jsx
@@ -1,12 +1,11 @@
-import React from 'react'
-import { withFormik } from 'formik'
-import { pick, isEmpty } from 'lodash'
+import React, { useState } from 'react'
+import { isEmpty } from 'lodash'
 import styled from 'styled-components'
-import { compose, withProps, withState, withHandlers } from 'recompose'
+// import { compose, withProps, withState, withHandlers } from 'recompose'
 import { Button, TextField, ValidatedFieldFormik } from '@pubsweet/ui'
 import { th } from '@pubsweet/ui-toolkit'
 import { AbstractField, RadioBox } from './builderComponents'
-import { Page, Heading } from './molecules/Page'
+import { Page, Heading } from './style'
 
 const nameText = input => <TextField {...input} />
 
@@ -22,20 +21,20 @@ export const Section = styled.div`
   margin: calc(${th('gridUnit')} * 6) 0;
 `
 
-const onSubmit = (values, { onSubmitFn, properties, mode }) => {
-  if (mode === 'create') {
-    onSubmitFn(Object.assign({}, values))
-  } else {
-    onSubmitFn({ id: properties.properties.id }, Object.assign({}, values))
-  }
-}
+// const onSubmit = (values, { handleSubmit, mode }) => {
+//   if (mode === 'create') {
+//     handleSubmit(Object.assign({}, values))
+//   } else {
+//     handleSubmit(values)
+//   }
+// }
 
 const FormProperties = ({
   handleSubmit,
   properties,
   mode,
-  selectPopup,
-  showPopupValue,
+  setPopup,
+  popup,
   values,
   setFieldValue,
 }) =>
@@ -73,7 +72,7 @@ const FormProperties = ({
             name="haspopup"
             onChange={(input, value) => {
               setFieldValue('haspopup', input)
-              selectPopup(input)
+              setPopup(input)
             }}
             options={[
               {
@@ -87,7 +86,7 @@ const FormProperties = ({
             ]}
           />
         </Section>
-        {showPopupValue === 'true' && [
+        {popup === 'true' && [
           <Section id="popup.title" key="popup.title">
             <Legend>Popup Title</Legend>
             <ValidatedFieldFormik component={nameText} name="popuptitle" />
@@ -110,32 +109,69 @@ const FormProperties = ({
     </Page>
   )
 
-export default compose(
-  withProps(({ properties }) => {
-    const paths = [
-      'id',
-      'name',
-      'description',
-      'popupdescription',
-      'popuptitle',
-      'haspopup',
-    ]
-    return {
-      initialValues: pick(properties.properties, paths),
-    }
-  }),
-  withState(
-    'showPopupValue',
-    'selectPopup',
-    ({ properties }) => (properties.properties || {}).haspopup,
-  ),
-  withHandlers({
-    changeShowPopup: ({ selectPopup }) => value => selectPopup(() => value),
-  }),
-  withFormik({
-    displayName: 'FormSubmit',
-    mapPropsToValues: data => data.properties.properties,
-    handleSubmit: (props, { props: { mode, onSubmitFn, properties } }) =>
-      onSubmit(props, { mode, onSubmitFn, properties }),
-  }),
-)(FormProperties)
+const FormPropertiesWrapper = ({
+  properties,
+  mode,
+  handleSubmit,
+  ...props
+}) => {
+  const [popup, setPopup] = useState((properties.properties || {}).haspopup)
+  return (
+    // <Formik
+    //   initialValues={pick(properties.properties, [
+    //     'id',
+    //     'name',
+    //     'description',
+    //     'popupdescription',
+    //     'popuptitle',
+    //     'haspopup',
+    //   ])}
+    //   onSubmit={(values) => {
+    //     onSubmit(values, { mode, handleSubmit })
+    //   }}
+    // >
+    //   {props => (
+    <FormProperties
+      handleSubmit={handleSubmit}
+      mode={mode}
+      popup={popup}
+      properties={properties}
+      setFieldValue={props.setFieldValue}
+      setPopup={setPopup}
+      values={props.values}
+    />
+    // )}
+    // </Formik>
+  )
+}
+
+export default FormPropertiesWrapper
+// export default compose(
+// withProps(({ properties }) => {
+//   const paths = [
+//     'id',
+//     'name',
+//     'description',
+//     'popupdescription',
+//     'popuptitle',
+//     'haspopup',
+//   ]
+//   return {
+//     initialValues: pick(properties.properties, paths),
+//   }
+// }),
+// withState(
+//   'showPopupValue',
+//   'selectPopup',
+//   ({ properties }) => (properties.properties || {}).haspopup,
+// ),
+// withHandlers({
+//   changeShowPopup: ({ selectPopup }) => value => selectPopup(() => value),
+// }),
+//   withFormik({
+//     displayName: 'FormSubmit',
+//     mapPropsToValues: data => data.properties.properties,
+//     handleSubmit: (props, { props: { mode, handleSubmit, properties } }) =>
+//       onSubmit(props, { mode, handleSubmit, properties }),
+//   }),
+// )(FormProperties)
diff --git a/app/components/component-formbuilder/src/components/atoms/Columns.js b/app/components/component-formbuilder/src/components/atoms/Columns.js
deleted file mode 100644
index f2f4e3d85877d1374ad5eb6d1d987eaf7dcee64d..0000000000000000000000000000000000000000
--- a/app/components/component-formbuilder/src/components/atoms/Columns.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import styled from 'styled-components'
-
-const Columns = styled.div`
-  display: grid;
-  grid-column-gap: 2em;
-  grid-template-areas: 'form details';
-  grid-template-columns: minmax(200px, 70ch) minmax(200px, 60ch);
-  justify-content: center;
-  overflow-y: scroll;
-`
-
-const Form = styled.div`
-  grid-area: form;
-`
-
-const Details = styled.div`
-  grid-area: details;
-
-  border-left: 1px solid black;
-  padding-left: 40px;
-`
-
-export { Columns, Form, Details }
diff --git a/app/components/component-formbuilder/src/components/builderComponents/Menu.js b/app/components/component-formbuilder/src/components/builderComponents/Menu.js
index 57e19821b65b88351c6b2a4a288e1732e2bb4ac8..4106846b3aec379206cea052d255628704ee7c2d 100644
--- a/app/components/component-formbuilder/src/components/builderComponents/Menu.js
+++ b/app/components/component-formbuilder/src/components/builderComponents/Menu.js
@@ -1,6 +1,6 @@
 import React from 'react'
 import { Select, TextField, ValidatedFieldFormik } from '@pubsweet/ui'
-import { Legend, Section } from '../styles'
+import { Legend, Section } from '../style'
 
 const ValidationMenu = input => (
   // const validations = useState([])
diff --git a/app/components/component-formbuilder/src/components/config/Elements.js b/app/components/component-formbuilder/src/components/config/Elements.js
index 25e529bb7aa9177d932cd119273c93d52473c551..6e087d0c194ff79c67fdcd0cb39a67075a983d10 100644
--- a/app/components/component-formbuilder/src/components/config/Elements.js
+++ b/app/components/component-formbuilder/src/components/config/Elements.js
@@ -89,6 +89,14 @@ const elements = {
     shortDescription: textfield,
     validate,
   },
+  LinksInput: {
+    id: textfield,
+    title: textfield,
+    name: textfield,
+    order: orderfield,
+    shortDescription: textfield,
+    validate,
+  },
   AbstractEditor: {
     id: textfield,
     title: textfield,
diff --git a/app/components/component-formbuilder/src/components/molecules/Page.js b/app/components/component-formbuilder/src/components/molecules/Page.js
deleted file mode 100644
index d9d9ed7c6c88bdd3eb67438e4c8e2922a4e92025..0000000000000000000000000000000000000000
--- a/app/components/component-formbuilder/src/components/molecules/Page.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import styled from 'styled-components'
-import { th } from '@pubsweet/ui-toolkit'
-
-const Page = styled.div`
-  margin: auto;
-  max-width: 60em;
-`
-
-const Section = styled.div`
-  margin: ${th('gridUnit')} 0;
-
-  &:not(:last-of-type) {
-    margin-bottom: calc(${th('gridUnit')} * 2);
-  }
-`
-
-const Heading = styled.div`
-  color: ${th('colorPrimary')};
-  font-family: ${th('fontReading')};
-  font-size: ${th('fontSizeHeading3')};
-  margin: ${th('gridUnit')} 0;
-  text-transform: uppercase;
-`
-
-const UploadContainer = styled.div`
-  display: flex;
-  justify-content: center;
-`
-
-export { Page, Section, Heading, UploadContainer }
diff --git a/app/components/component-formbuilder/src/components/style.js b/app/components/component-formbuilder/src/components/style.js
new file mode 100644
index 0000000000000000000000000000000000000000..c2b03dbb300e61674b9de8d343c0d5b9e8d906a9
--- /dev/null
+++ b/app/components/component-formbuilder/src/components/style.js
@@ -0,0 +1,56 @@
+import styled from 'styled-components'
+import { th } from '@pubsweet/ui-toolkit'
+
+export const Section = styled.div`
+  margin: calc(${th('gridUnit')} * 6) 0;
+`
+
+export const Legend = styled.div`
+  font-size: ${th('fontSizeBase')};
+  font-weight: 600;
+  margin-bottom: ${({ space, theme }) => space && theme.gridUnit};
+`
+
+const Columns = styled.div`
+  display: grid;
+  grid-column-gap: 2em;
+  grid-template-areas: 'form details';
+  grid-template-columns: 1fr 1fr;
+  justify-content: center;
+`
+
+const Form = styled.div`
+  grid-area: form;
+`
+
+const Details = styled.div`
+  grid-area: details;
+  margin-top: 48px;
+`
+
+export { Columns, Form, Details }
+
+const Page = styled.div``
+
+// const Section = styled.div`
+//   margin: ${th('gridUnit')} 0;
+
+//   &:not(:last-of-type) {
+//     margin-bottom: calc(${th('gridUnit')} * 2);
+//   }
+// `
+
+const Heading = styled.div`
+  color: ${th('colorPrimary')};
+  font-family: ${th('fontReading')};
+  font-size: ${th('fontSizeHeading3')};
+  margin: ${th('gridUnit')} 0;
+  text-transform: uppercase;
+`
+
+const UploadContainer = styled.div`
+  display: flex;
+  justify-content: center;
+`
+
+export { Page, Heading, UploadContainer }
diff --git a/app/components/component-formbuilder/src/components/styles/index.js b/app/components/component-formbuilder/src/components/styles/index.js
deleted file mode 100644
index 96872aa8d8d415c2f0aaec78d036de0b5ef0a04e..0000000000000000000000000000000000000000
--- a/app/components/component-formbuilder/src/components/styles/index.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import styled from 'styled-components'
-import { th } from '@pubsweet/ui-toolkit'
-
-export const Section = styled.div`
-  margin: calc(${th('gridUnit')} * 6) 0;
-`
-
-export const Legend = styled.div`
-  font-size: ${th('fontSizeBase')};
-  font-weight: 600;
-  margin-bottom: ${({ space, theme }) => space && theme.gridUnit};
-`
diff --git a/app/components/component-formbuilder/src/index.js b/app/components/component-formbuilder/src/index.js
index 2d58c3f7e526173abd2e881b9d99053d83742677..5e7e164a43cc091c0380a47becf8b35353fec7a3 100644
--- a/app/components/component-formbuilder/src/index.js
+++ b/app/components/component-formbuilder/src/index.js
@@ -1,3 +1,3 @@
-module.exports = {
-  server: () => require('./server/formRequestBackend'),
-}
+import FormBuilder from './components'
+
+export default FormBuilder
diff --git a/app/components/component-formbuilder/src/redux/FormBuilder.js b/app/components/component-formbuilder/src/redux/FormBuilder.js
deleted file mode 100644
index 26325d6da707cd4c3b480215d436937bdebf7276..0000000000000000000000000000000000000000
--- a/app/components/component-formbuilder/src/redux/FormBuilder.js
+++ /dev/null
@@ -1,135 +0,0 @@
-// import * as api from 'pubsweet-client/src/helpers/api'
-
-// export const GET_FORM_REQUEST = 'GET_FORM_REQUEST'
-// export const GET_FORM_SUCCESS = 'GET_FORM_SUCCESS'
-// export const GET_FORM_FAILURE = 'GET_FORM_FAILURE'
-
-// function getFormRequest(project, version) {
-//   return {
-//     type: GET_FORM_REQUEST,
-//   }
-// }
-
-// function getFormSuccess(forms) {
-//   return {
-//     type: GET_FORM_SUCCESS,
-//     forms,
-//   }
-// }
-
-// function getFormFailure(error) {
-//   return {
-//     type: GET_FORM_FAILURE,
-//     error,
-//   }
-// }
-
-// export function getForms() {
-//   return dispatch => {
-//     dispatch(getFormRequest())
-
-//     return api
-//       .get('/get-forms', {})
-//       .then(result => {
-//         dispatch(getFormSuccess(result))
-//       })
-//       .catch(error => dispatch(getFormFailure(error)))
-//   }
-// }
-
-// export function getForm(formId) {
-//   return dispatch => {
-//     dispatch(getFormRequest())
-
-//     return api
-//       .get(`/get-form/${formId}`, {})
-//       .then(result => {
-//         dispatch(getFormSuccess(result))
-//       })
-//       .catch(error => dispatch(getFormFailure(error)))
-//   }
-// }
-
-// export function updateForms(form, properties) {
-//   return dispatch => {
-//     dispatch(getFormRequest())
-
-//     return api
-//       .update(`/update-forms/${form.id}`, properties)
-//       .then(result => {
-//         dispatch(getFormSuccess(result))
-//       })
-//       .catch(error => dispatch(getFormFailure(error)))
-//   }
-// }
-
-// export function updateElements(form, properties) {
-//   return dispatch => {
-//     dispatch(getFormRequest())
-
-//     return api
-//       .update(
-//         `/update-forms/${form.id}/element/${properties.children.id}`,
-//         properties,
-//       )
-//       .then(result => {
-//         dispatch(getFormSuccess(result))
-//       })
-//       .catch(error => dispatch(getFormFailure(error)))
-//   }
-// }
-
-// export function deleteForms(form) {
-//   return dispatch => {
-//     dispatch(getFormRequest())
-
-//     return api
-//       .remove(`/delete-forms/${form.id}`)
-//       .then(result => {
-//         dispatch(getFormSuccess(result))
-//       })
-//       .catch(error => dispatch(getFormFailure(error)))
-//   }
-// }
-
-// export function deleteElements(form, element) {
-//   return dispatch => {
-//     dispatch(getFormRequest())
-
-//     return api
-//       .remove(`/delete-forms/${form.id}/elements/${element.id}`)
-//       .then(result => {
-//         dispatch(getFormSuccess(result))
-//       })
-//       .catch(error => dispatch(getFormFailure(error)))
-//   }
-// }
-
-// export function createForms(properties) {
-//   return dispatch => {
-//     dispatch(getFormRequest())
-
-//     return api
-//       .create('/create-forms', properties)
-//       .then(result => {
-//         dispatch(getFormSuccess(result))
-//       })
-//       .catch(error => dispatch(getFormFailure(error)))
-//   }
-// }
-
-// const initialState = {}
-// export default (state = initialState, action) => {
-//   switch (action.type) {
-//     case GET_FORM_SUCCESS:
-//       return {
-//         forms: action.forms.forms,
-//       }
-
-//     case GET_FORM_FAILURE:
-//       return { error: action.error }
-
-//     default:
-//       return state
-//   }
-// }
diff --git a/app/components/component-formbuilder/src/server/formRequestBackend.js b/app/components/component-formbuilder/src/server/formRequestBackend.js
deleted file mode 100644
index 898f4625d237d1262aabd003ecee1aade6134d88..0000000000000000000000000000000000000000
--- a/app/components/component-formbuilder/src/server/formRequestBackend.js
+++ /dev/null
@@ -1,204 +0,0 @@
-// const { pick } = require('lodash')
-const config = require('config')
-const passport = require('passport')
-// const logger = require('@pubsweet/logger')
-// const User = require('pubsweet-server/src/models/User')
-// const authsome = require('pubsweet-server/src/helpers/authsome')
-// const AuthorizationError = require('pubsweet-server/src/errors/AuthorizationError')
-const fs = require('fs')
-// const filepath = require('path')
-const { readFiles, mkdirp } = require('./util')
-
-const authBearer = passport.authenticate('bearer', { session: false })
-
-const mergeFiles = path =>
-  readFiles(path).then(files => {
-    const forms = []
-    files.forEach((item, index) => {
-      // const { name } = filepath.parse(item.filename)
-      const content = JSON.parse(item.content)
-      if (!content.name) return
-      forms.push(content)
-    })
-    return { forms }
-  })
-
-module.exports = app => {
-  app.get('/api/get-forms', authBearer, async (req, res, next) => {
-    try {
-      const folderPath = `${config.get(
-        'pubsweet-component-xpub-formbuilder.path',
-      )}/`
-
-      mkdirp(folderPath)
-      mergeFiles(folderPath).then(forms =>
-        res.send({
-          forms,
-        }),
-      )
-    } catch (err) {
-      next(err)
-    }
-  })
-
-  app.get('/api/get-form/:formId', authBearer, async (req, res, next) => {
-    try {
-      const { formId } = req.params
-      const folderPath = `${config.get(
-        'pubsweet-component-xpub-formbuilder.path',
-      )}/`
-      const path = `${folderPath}${formId}.json`
-      const forms = JSON.parse(fs.readFileSync(path, 'utf8'))
-
-      res.send({
-        forms,
-      })
-    } catch (err) {
-      next(err)
-    }
-  })
-
-  app.patch(
-    '/api/update-forms/:formId/element/:elementId',
-    authBearer,
-    async (req, res, next) => {
-      try {
-        const { formId, elementId } = req.params
-        const content = req.body
-        const folderPath = `${config.get(
-          'pubsweet-component-xpub-formbuilder.path',
-        )}/`
-        const path = `${folderPath}${formId}.json`
-        const forms = JSON.parse(fs.readFileSync(path, 'utf8'))
-        if (!forms.children) {
-          forms.children = [content.children]
-        } else if (forms.children.some(e => e.id === elementId)) {
-          const children = forms.children.map(value =>
-            value.id === elementId ? content.children : value,
-          )
-          forms.children = children
-        } else {
-          forms.children.push(content.children)
-        }
-
-        fs.writeFileSync(path, JSON.stringify(forms))
-        mergeFiles(folderPath).then(forms =>
-          res.send({
-            forms,
-          }),
-        )
-      } catch (err) {
-        next(err)
-      }
-    },
-  )
-
-  app.patch('/api/update-forms/:formId', authBearer, async (req, res, next) => {
-    try {
-      const { formId } = req.params
-      let content = req.body
-      const folderPath = `${config.get(
-        'pubsweet-component-xpub-formbuilder.path',
-      )}/`
-      let path = `${folderPath}${formId}.json`
-
-      if (fs.existsSync(path)) {
-        let forms = JSON.parse(fs.readFileSync(path, 'utf8'))
-        forms = Object.assign(forms, content)
-        content = forms
-        if (formId !== content.id) {
-          fs.unlinkSync(path)
-          path = `${folderPath}${content.id}.json`
-        }
-      }
-
-      fs.writeFileSync(path, JSON.stringify(content))
-      mergeFiles(folderPath).then(forms =>
-        res.send({
-          forms,
-        }),
-      )
-    } catch (err) {
-      next(err)
-    }
-  })
-
-  app.post('/api/create-forms', authBearer, async (req, res, next) => {
-    try {
-      const content = req.body
-      const folderPath = `${config.get(
-        'pubsweet-component-xpub-formbuilder.path',
-      )}/`
-      const path = `${folderPath}/${content.id}.json`
-
-      if (!fs.existsSync(path)) {
-        mkdirp(folderPath)
-        fs.writeFileSync(path, JSON.stringify(content))
-      }
-
-      mergeFiles(folderPath).then(forms =>
-        res.send({
-          forms,
-        }),
-      )
-    } catch (err) {
-      next(err)
-    }
-  })
-
-  app.delete(
-    '/api/delete-forms/:formId/elements/:elementId',
-    authBearer,
-    async (req, res, next) => {
-      try {
-        const { formId, elementId } = req.params
-        const folderPath = `${config.get(
-          'pubsweet-component-xpub-formbuilder.path',
-        )}/`
-
-        const path = `${folderPath}/${formId}.json`
-        const forms = JSON.parse(fs.readFileSync(path, 'utf8'))
-
-        if (forms.children) {
-          const children = forms.children.filter(el => el.id !== elementId)
-          forms.children = children
-          fs.writeFileSync(path, JSON.stringify(forms))
-        }
-
-        mergeFiles(folderPath).then(forms =>
-          res.send({
-            forms,
-          }),
-        )
-      } catch (err) {
-        next(err)
-      }
-    },
-  )
-
-  app.delete(
-    '/api/delete-forms/:formId',
-    authBearer,
-    async (req, res, next) => {
-      try {
-        const { formId } = req.params
-        const folderPath = `${config.get(
-          'pubsweet-component-xpub-formbuilder.path',
-        )}/`
-        const path = `${folderPath}${formId}.json`
-
-        if (fs.existsSync(path)) {
-          fs.unlinkSync(path)
-        }
-
-        mergeFiles(folderPath).then(forms =>
-          res.send({
-            forms,
-          }),
-        )
-      } catch (err) {
-        next(err)
-      }
-    },
-  )
-}
diff --git a/app/components/component-formbuilder/src/server/util.js b/app/components/component-formbuilder/src/server/util.js
deleted file mode 100644
index 32976c84cf573aef9f389b5d7230685ed3e7604c..0000000000000000000000000000000000000000
--- a/app/components/component-formbuilder/src/server/util.js
+++ /dev/null
@@ -1,58 +0,0 @@
-const fs = require('fs')
-const path = require('path')
-
-const Util = {}
-
-const promiseAllP = (items, block) => {
-  const promises = []
-  items.forEach((item, index) => {
-    const prom = () =>
-      new Promise((resolve, reject) =>
-        block.apply(this, [item, index, resolve, reject]),
-      )
-    promises.push(prom(item, index))
-  })
-  return Promise.all(promises)
-}
-
-Util.mkdirp = dir =>
-  path
-    .resolve(dir)
-    .split(path.sep)
-    .reduce((acc, cur) => {
-      const currentPath = path.normalize(acc + path.sep + cur)
-      try {
-        fs.statSync(currentPath)
-      } catch (e) {
-        if (e.code === 'ENOENT') {
-          fs.mkdirSync(currentPath)
-        } else {
-          throw e
-        }
-      }
-      return currentPath
-    }, '')
-
-Util.readFiles = dirname =>
-  new Promise((resolve, reject) => {
-    fs.readdir(dirname, (err, filenames) => {
-      if (err) return reject(err)
-      return promiseAllP(filenames, (filename, index, resolve, reject) =>
-        fs.readFile(
-          path.resolve(dirname, filename),
-          'utf-8',
-          (err, content) => {
-            if (err) return reject(err)
-            return resolve({
-              filename,
-              content,
-            })
-          },
-        ),
-      )
-        .then(results => resolve(results))
-        .catch(error => reject(error))
-    })
-  })
-
-module.exports = Util
diff --git a/app/components/component-review/src/components/metadata/ReviewMetadata.js b/app/components/component-review/src/components/metadata/ReviewMetadata.js
index 25bec27634eff8829a8f23cb22c0e250db654579..0b418eaf980aa67531b6a117597043d52caf6db7 100644
--- a/app/components/component-review/src/components/metadata/ReviewMetadata.js
+++ b/app/components/component-review/src/components/metadata/ReviewMetadata.js
@@ -44,7 +44,19 @@ const getSupplementaryFiles = supplementary =>
 
 const showFieldData = (manuscript, fieldName) => {
   const data = get(manuscript, fieldName)
-  return Array.isArray(data) ? data.join(', ') : data
+  // TODO: Make this generic somehow. Perhaps with an additional fieldType?
+  if (Array.isArray(data) && fieldName === 'submission.links') {
+    return data.map(link => (
+      <p>
+        <a href={link.url} rel="noopener noreferrer" target="_blank">
+          {link.url}
+        </a>
+      </p>
+    ))
+  } else if (Array.isArray(data)) {
+    return data.join(', ')
+  }
+  return data
 }
 // Due to migration to new Data Model
 // Attachement component needs different data structure to work
diff --git a/app/components/component-submit/src/components/FormTemplate.js b/app/components/component-submit/src/components/FormTemplate.js
index f23d8e4ee663a89668791247bbb69b054c8be5c2..1610b24e4eec4e1e16d0981ccd9ce2f8980acb84 100644
--- a/app/components/component-submit/src/components/FormTemplate.js
+++ b/app/components/component-submit/src/components/FormTemplate.js
@@ -15,6 +15,7 @@ import { AbstractEditor } from 'xpub-edit'
 import { Section as Container, Select, FilesUpload } from '../../../shared'
 import { Heading1, Section, Legend, SubNote } from '../style'
 import AuthorsInput from './AuthorsInput'
+import LinksInput from './LinksInput'
 import Confirm from './Confirm'
 
 // TODO: https://github.com/formium/formik/issues/146#issuecomment-474775723
@@ -100,6 +101,7 @@ elements.AbstractEditor = ({
 
 elements.AuthorsInput = AuthorsInput
 elements.Select = Select
+elements.LinksInput = LinksInput
 
 const rejectProps = (obj, keys) =>
   Object.keys(obj)
diff --git a/app/components/component-submit/src/components/LinksInput.js b/app/components/component-submit/src/components/LinksInput.js
new file mode 100644
index 0000000000000000000000000000000000000000..06365756317dc06141cc85dc0f4220a124cc9089
--- /dev/null
+++ b/app/components/component-submit/src/components/LinksInput.js
@@ -0,0 +1,119 @@
+import React, { useRef, useEffect } from 'react'
+import styled from 'styled-components'
+import { FieldArray } from 'formik'
+import { cloneDeep, set, get } from 'lodash'
+import { TextField, Button, ValidatedFieldFormik } from '@pubsweet/ui'
+// import { minSize } from 'xpub-validators'
+
+// const minSize1 = minSize(1)
+
+const Inline = styled.div`
+  display: inline-block;
+  margin-right: 30px;
+`
+
+const UnbulletedList = styled.div`
+  list-style-type: none;
+`
+
+const Spacing = styled.div`
+  padding: 15px 0px;
+`
+
+const Link = styled.div`
+  padding-bottom: 10px;
+`
+
+const URLInput = input => (
+  <TextField label="URL" placeholder="Enter a URL" {...input} />
+)
+
+const LinksInput = ({
+  form,
+  remove,
+  push,
+  value,
+  name,
+  onChange,
+  ...props
+}) => {
+  const valuesRef = useRef(form.values)
+
+  useEffect(() => {
+    valuesRef.current = form.values
+  }, [form.values])
+
+  const onChangeFn = event => {
+    form.setFieldValue(event.target?.name, event.target?.value, true)
+
+    const data = cloneDeep(valuesRef.current)
+    set(data, event.target?.name, event.target?.value)
+    onChange(get(data, name))
+  }
+  return (
+    <ul>
+      <UnbulletedList>
+        <li>
+          <Button
+            onClick={() =>
+              push({
+                url: '',
+              })
+            }
+            primary
+            type="button"
+          >
+            {value && value.length ? 'Add another link' : 'Add a link'}
+          </Button>
+        </li>
+        {(value || []).map((link, index) => (
+          // TODO: Use a different key.
+          // eslint-disable-next-line react/no-array-index-key
+          <li key={`link-${index}`}>
+            <Spacing>
+              <Link>
+                Link:&nbsp;
+                {value.length > 1 && (
+                  <Button
+                    onClick={() => {
+                      remove(index)
+                    }}
+                    type="button"
+                  >
+                    Remove
+                  </Button>
+                )}
+              </Link>
+              <div>
+                <Inline>
+                  <ValidatedFieldFormik
+                    component={URLInput}
+                    name={`${name}.${index}.url`}
+                    onChange={onChangeFn}
+                    // TODO: validate={minSize1}
+                  />
+                </Inline>
+              </div>
+            </Spacing>
+          </li>
+        ))}
+      </UnbulletedList>
+    </ul>
+  )
+}
+const LinksInputFieldArray = ({ onChange, name, value }) => (
+  <FieldArray name={name}>
+    {({ form, remove, push }) => (
+      <LinksInput
+        form={form}
+        name={name}
+        onChange={onChange}
+        push={push}
+        remove={remove}
+        value={value}
+      />
+    )}
+  </FieldArray>
+)
+
+export default LinksInputFieldArray
diff --git a/app/components/component-submit/src/components/SubmitPage.js b/app/components/component-submit/src/components/SubmitPage.js
index 4da4c390a62708307b6e50156851e2e87826c011..9012c140e8def4803b90ec562891c8279bd049de 100644
--- a/app/components/component-submit/src/components/SubmitPage.js
+++ b/app/components/component-submit/src/components/SubmitPage.js
@@ -136,7 +136,7 @@ const SubmitPage = ({ match, history, ...props }) => {
   const [update] = useMutation(updateMutation)
 
   if (loading) return <Spinner />
-  if (error) return error
+  if (error) return JSON.stringify(error)
 
   const manuscript = data?.manuscript
   const getFile = data?.getFile
diff --git a/app/storage/forms/submit.json b/app/storage/forms/submit.json
index a67b934cbb5e32ae0417aa011a8a170ec9752a3b..f5036936a3338d223c35cd982d8568b7b5bd84db 100644
--- a/app/storage/forms/submit.json
+++ b/app/storage/forms/submit.json
@@ -128,12 +128,12 @@
       "order": "5"
     },
     {
-      "title": "Links",
+      "title": "Research object links",
       "id": "1591192678688",
-      "component": "TextField",
+      "component": "LinksInput",
       "name": "submission.links",
       "placeholder": "Enter your links, separated by commas",
-      "order": "9",
+      "order": "-1",
       "parse": {
         "value": "split",
         "label": "Split"
diff --git a/config/permissions.js b/config/permissions.js
index ebd6f8dc0a311248b2e4b18e0b50ce4b27638727..7a88852adb4ef7b1ef6d88c6c8f0669d6ed03760 100644
--- a/config/permissions.js
+++ b/config/permissions.js
@@ -24,10 +24,12 @@ const permissions = shield(
   {
     Query: {
       paginatedManuscripts: isAdmin,
+      detailsForURL: allow,
     },
     Mutation: {
       createManuscript: isAuthenticated,
     },
+    URLMetadata: allow,
     // Fruit: isAuthenticated,
     // Customer: isAdmin,
   },
diff --git a/cypress/dumps/decision_completed.sql b/cypress/dumps/decision_completed.sql
index 131e3333d986432395c5488837c6c94cbd0f1a5b..8ceff3ede03b13ae5518c3c24c0343ecbd99bfda 100644
--- a/cypress/dumps/decision_completed.sql
+++ b/cypress/dumps/decision_completed.sql
@@ -420,8 +420,8 @@ INSERT INTO pgboss.version (version) VALUES ('11');
 -- Data for Name: channels; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('afd95c06-011b-46e3-9985-964283d51c4a', '93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Manuscript discussion', 'all');
-INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('75540565-6619-495f-a8c5-d6bf11ece72d', '93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Editorial discussion', 'editorial');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('6894e14e-1e09-47b5-90d0-ce0e6574cca3', '78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Manuscript discussion', 'all');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('48285109-8d43-4443-aa0f-be6e16dec11e', '78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Editorial discussion', 'editorial');
 
 
 --
@@ -434,6 +434,7 @@ INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) V
 -- Data for Name: files; Type: TABLE DATA; Schema: public; Owner: test
 --
 
+INSERT INTO public.files (id, created, updated, label, file_type, filename, url, mime_type, size, type, manuscript_id, review_comment_id) VALUES ('eee84304-527d-426f-a083-4cbac8d5f102', '2020-08-15 23:39:54.916+02', '2020-08-15 23:39:54.916+02', NULL, 'supplementary', 'test-pdf.pdf', '/static/uploads/b251794b90f9a5f9de097babebb81762.pdf', 'application/pdf', 142400, 'file', '78f68e7b-ac0c-44b1-97ca-f30044b53553', NULL);
 
 
 --
@@ -453,7 +454,7 @@ INSERT INTO public.identities (id, user_id, created, updated, type, identifier,
 -- Data for Name: manuscripts; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.9+02', '2020-08-13 15:12:29.223+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'accepted', 'accepted', NULL, NULL, '{"notes": [{"content": "", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions"}], "title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": "https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml", "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keyword", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
+INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.481+02', '2020-08-15 23:41:44.723+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'accepted', 'accepted', NULL, NULL, '{"title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": [{"url": "https://doi.org/10.6084/m9.figshare.913521.v1"}, {"url": "https://github.com/jure/mathtype_to_mathml"}], "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keywords", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
 
 
 --
@@ -487,42 +488,44 @@ INSERT INTO public.migrations (id, run_at) VALUES ('1596838897-files.sql', '2020
 -- Data for Name: review_comments; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('f73fdbd8-6fb6-4d7a-a74a-ab2c2c07b572', '2020-08-13 15:09:33.838+02', '2020-08-13 15:09:33.838+02', '5a06fcf3-d368-4f65-917c-1acdb73fcc71', NULL, '<p>Great paper, congratulations! Gale Davis</p>', 'review', 'ReviewComment');
-INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('5c86da62-bc7f-4d23-8968-62bd5a56a52b', '2020-08-13 15:09:34.88+02', '2020-08-13 15:09:34.88+02', '5a06fcf3-d368-4f65-917c-1acdb73fcc71', NULL, '<p>This is a very important paper. Gale Davis</p>', 'confidential', 'ReviewComment');
-INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('ef96b249-dab6-4f7e-b24e-571b340b9b41', '2020-08-13 15:09:38.761+02', '2020-08-13 15:09:38.761+02', '7c6cd2f6-c23e-4902-96ac-df4801ac3d0a', NULL, '<p>Great paper, congratulations! Sherry Crofoot</p>', 'review', 'ReviewComment');
-INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('04a8a70e-7787-4fd6-bdca-bfac3aa86951', '2020-08-13 15:09:40.155+02', '2020-08-13 15:09:40.155+02', '7c6cd2f6-c23e-4902-96ac-df4801ac3d0a', NULL, '<p>This is a very important paper. Sherry Crofoot</p>', 'confidential', 'ReviewComment');
-INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('7ccda096-07c2-4569-ad0f-5b3574db205e', '2020-08-13 15:09:44.046+02', '2020-08-13 15:09:44.046+02', '3a34e0ef-6695-4268-b901-60de20f9cf4e', NULL, '<p>Great paper, congratulations! Elaine Barnes</p>', 'review', 'ReviewComment');
-INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('c8b7d1c9-aef2-47d0-a9ad-c9d46706e79e', '2020-08-13 15:09:45.548+02', '2020-08-13 15:09:45.548+02', '3a34e0ef-6695-4268-b901-60de20f9cf4e', NULL, '<p>This is a very important paper. Elaine Barnes</p>', 'confidential', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('f27dcbeb-fa71-4037-81cb-d97f08b42e52', '2020-08-15 23:41:00.217+02', '2020-08-15 23:41:00.217+02', '8970d68a-c5ec-4e4e-bd7d-407449f7cf2c', NULL, '<p>Great paper, congratulations! Gale Davis</p>', 'review', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('24236d62-adbe-4ef1-b671-734297c47570', '2020-08-15 23:41:01.33+02', '2020-08-15 23:41:01.33+02', '8970d68a-c5ec-4e4e-bd7d-407449f7cf2c', NULL, '<p>This is a very important paper. Gale Davis</p>', 'confidential', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('fb52cc08-f7b7-4720-88e2-4536f02599c4', '2020-08-15 23:41:05.761+02', '2020-08-15 23:41:05.761+02', '394fce94-31b2-4b3d-9182-baf35759e1f6', NULL, '<p>Great paper, congratulations! Sherry Crofoot</p>', 'review', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('8adf1f76-8bcd-4967-844f-dfa9b5ae652f', '2020-08-15 23:41:07.444+02', '2020-08-15 23:41:07.444+02', '394fce94-31b2-4b3d-9182-baf35759e1f6', NULL, '<p>This is a very important paper. Sherry Crofoot</p>', 'confidential', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('e4beb5ef-0c45-4256-aa8b-2982dd851ccd', '2020-08-15 23:41:12.853+02', '2020-08-15 23:41:12.853+02', 'dcf6e734-c549-49b4-a60d-18a9473762fb', NULL, '<p>Great paper, congratulations! Elaine Barnes</p>', 'review', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('ec71b59e-baa8-4f7c-ac9e-e6365892fe8f', '2020-08-15 23:41:15.069+02', '2020-08-15 23:41:15.069+02', 'dcf6e734-c549-49b4-a60d-18a9473762fb', NULL, '<p>This is a very important paper. Elaine Barnes</p>', 'confidential', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('5bd9e18d-7618-46d1-b557-9c113be93628', '2020-08-15 23:41:44.336+02', '2020-08-15 23:41:44.336+02', '02997d7f-f041-462e-a2c1-4693e3dc79aa', NULL, '<p>Great paper, dear authors, congratulations!</p>', 'decision', 'ReviewComment');
 
 
 --
 -- Data for Name: reviews; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('5a06fcf3-d368-4f65-917c-1acdb73fcc71', '2020-08-13 15:09:32.358+02', '2020-08-13 15:09:34.925+02', 'accepted', false, '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
-INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('7c6cd2f6-c23e-4902-96ac-df4801ac3d0a', '2020-08-13 15:09:36.501+02', '2020-08-13 15:09:40.159+02', 'accepted', false, '0da0bbec-9261-4706-b990-0c10aa3cc6b4', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
-INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('3a34e0ef-6695-4268-b901-60de20f9cf4e', '2020-08-13 15:09:41.911+02', '2020-08-13 15:09:45.561+02', 'accepted', false, '85e1300e-003c-4e96-987b-23812f902477', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
-INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('7d962c82-322e-4b02-8981-5f26479433ab', '2020-08-13 15:12:28.737+02', '2020-08-13 15:12:28.737+02', 'accepted', true, '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('8970d68a-c5ec-4e4e-bd7d-407449f7cf2c', '2020-08-15 23:40:58.391+02', '2020-08-15 23:41:01.335+02', 'accepted', false, '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '78f68e7b-ac0c-44b1-97ca-f30044b53553', 'Review');
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('394fce94-31b2-4b3d-9182-baf35759e1f6', '2020-08-15 23:41:03.424+02', '2020-08-15 23:41:07.45+02', 'accepted', false, '0da0bbec-9261-4706-b990-0c10aa3cc6b4', '78f68e7b-ac0c-44b1-97ca-f30044b53553', 'Review');
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('dcf6e734-c549-49b4-a60d-18a9473762fb', '2020-08-15 23:41:09.784+02', '2020-08-15 23:41:15.077+02', 'accepted', false, '85e1300e-003c-4e96-987b-23812f902477', '78f68e7b-ac0c-44b1-97ca-f30044b53553', 'Review');
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('6157d867-91c3-44ee-8f7a-1ef11b3a5679', '2020-08-15 23:41:44.12+02', '2020-08-15 23:41:44.12+02', 'accepted', true, '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '78f68e7b-ac0c-44b1-97ca-f30044b53553', 'Review');
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('02997d7f-f041-462e-a2c1-4693e3dc79aa', '2020-08-15 23:41:44.331+02', '2020-08-15 23:41:44.331+02', NULL, true, '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '78f68e7b-ac0c-44b1-97ca-f30044b53553', 'Review');
 
 
 --
 -- Data for Name: team_members; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('6505126d-2ecd-498b-a27d-18eafbdbc8a6', '2020-08-12 16:16:44.923+02', '2020-08-12 16:16:44.923+02', NULL, 'c31e3116-6176-45b5-b52c-6f7cbfc86007', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('031ae180-e94e-458d-86b7-f2dc1d456e2e', '2020-08-12 16:17:14.795+02', '2020-08-12 16:17:14.795+02', NULL, 'bf800912-56c4-44eb-b425-64e51a9824fe', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('c4ac4fc0-3fae-43b0-89ee-bf7f8fd9885f', '2020-08-12 16:17:43.262+02', '2020-08-13 15:09:41.898+02', 'completed', '0719d132-bb6c-49d9-9122-7911a48cfd60', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('69e896c6-0fdb-421d-a638-1ef7670c03c9', '2020-08-12 16:17:43.884+02', '2020-08-13 15:09:41.898+02', 'completed', '0719d132-bb6c-49d9-9122-7911a48cfd60', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('12c33ef5-4fc3-4d48-8309-a90c0461113c', '2020-08-12 16:17:44.547+02', '2020-08-13 15:09:45.782+02', 'completed', '0719d132-bb6c-49d9-9122-7911a48cfd60', '85e1300e-003c-4e96-987b-23812f902477', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('21c3e56c-c99b-4427-8789-e9d1120b64dc', '2020-08-15 23:39:31.49+02', '2020-08-15 23:39:31.49+02', NULL, '55e9d201-74a0-407f-b8e4-49da8c96ea85', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('5c51df7a-df3a-4d01-b778-883e2bf77420', '2020-08-15 23:40:09.007+02', '2020-08-15 23:40:09.007+02', NULL, '3148b252-255f-4bb7-b13e-6a0c8b02f2c7', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('ef82f708-f20c-48b2-81b7-a82b5bc9365f', '2020-08-15 23:40:39.581+02', '2020-08-15 23:41:09.713+02', 'completed', 'b9bd63d9-1dd6-413c-ba6b-35600505c239', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('26660f93-cb5a-4050-b31e-4068ee72614b', '2020-08-15 23:40:40.42+02', '2020-08-15 23:41:09.713+02', 'completed', 'b9bd63d9-1dd6-413c-ba6b-35600505c239', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('5e98ba3c-730d-4aa5-a2d7-4e95454dfd14', '2020-08-15 23:40:41.565+02', '2020-08-15 23:41:15.341+02', 'completed', 'b9bd63d9-1dd6-413c-ba6b-35600505c239', '85e1300e-003c-4e96-987b-23812f902477', NULL);
 
 
 --
 -- Data for Name: teams; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('c31e3116-6176-45b5-b52c-6f7cbfc86007', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Author', 'author', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
-INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('bf800912-56c4-44eb-b425-64e51a9824fe', '2020-08-12 16:17:14.789+02', '2020-08-12 16:17:14.789+02', 'Senior Editor', 'seniorEditor', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
-INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('0719d132-bb6c-49d9-9122-7911a48cfd60', '2020-08-12 16:17:43.258+02', '2020-08-13 15:09:41.898+02', 'Reviewers', 'reviewer', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('55e9d201-74a0-407f-b8e4-49da8c96ea85', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Author', 'author', NULL, NULL, NULL, 'team', '78f68e7b-ac0c-44b1-97ca-f30044b53553');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('3148b252-255f-4bb7-b13e-6a0c8b02f2c7', '2020-08-15 23:40:09.001+02', '2020-08-15 23:40:09.001+02', 'Senior Editor', 'seniorEditor', NULL, NULL, NULL, 'team', '78f68e7b-ac0c-44b1-97ca-f30044b53553');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('b9bd63d9-1dd6-413c-ba6b-35600505c239', '2020-08-15 23:40:39.574+02', '2020-08-15 23:41:09.713+02', 'Reviewers', 'reviewer', NULL, NULL, NULL, 'team', '78f68e7b-ac0c-44b1-97ca-f30044b53553');
 
 
 --
@@ -530,12 +533,12 @@ INSERT INTO public.teams (id, created, updated, name, role, members, owners, glo
 --
 
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.597+02', '2020-07-24 16:43:54.939+02', NULL, NULL, '000000032536230X', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser5.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-12 16:17:13.598+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-08-12 16:17:14.868+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.973+02', '2020-08-13 15:09:35.913+02', NULL, NULL, '0000000159567341', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser4.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-08-13 15:09:41.198+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('85e1300e-003c-4e96-987b-23812f902477', '2020-07-21 16:35:38.381+02', '2020-08-13 15:09:46.741+02', NULL, NULL, '0000000294294446', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser1.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-08-13 15:12:29.67+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-15 23:40:06.938+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-08-15 23:40:09.104+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.973+02', '2020-08-15 23:41:02.706+02', NULL, NULL, '0000000159567341', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser4.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-08-15 23:41:08.741+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('85e1300e-003c-4e96-987b-23812f902477', '2020-07-21 16:35:38.381+02', '2020-08-15 23:41:17.176+02', NULL, NULL, '0000000294294446', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser1.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-08-15 23:41:45.59+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
 
 
 --
diff --git a/cypress/dumps/reviewers_invited.sql b/cypress/dumps/reviewers_invited.sql
index 3733f35db91d178c49e70b354276c2dedc16d5b7..1b7e164bd9582b89a37450e0ebdf30ef7226045a 100644
--- a/cypress/dumps/reviewers_invited.sql
+++ b/cypress/dumps/reviewers_invited.sql
@@ -420,8 +420,8 @@ INSERT INTO pgboss.version (version) VALUES ('11');
 -- Data for Name: channels; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('afd95c06-011b-46e3-9985-964283d51c4a', '93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Manuscript discussion', 'all');
-INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('75540565-6619-495f-a8c5-d6bf11ece72d', '93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Editorial discussion', 'editorial');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('6894e14e-1e09-47b5-90d0-ce0e6574cca3', '78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Manuscript discussion', 'all');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('48285109-8d43-4443-aa0f-be6e16dec11e', '78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Editorial discussion', 'editorial');
 
 
 --
@@ -434,6 +434,7 @@ INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) V
 -- Data for Name: files; Type: TABLE DATA; Schema: public; Owner: test
 --
 
+INSERT INTO public.files (id, created, updated, label, file_type, filename, url, mime_type, size, type, manuscript_id, review_comment_id) VALUES ('eee84304-527d-426f-a083-4cbac8d5f102', '2020-08-15 23:39:54.916+02', '2020-08-15 23:39:54.916+02', NULL, 'supplementary', 'test-pdf.pdf', '/static/uploads/b251794b90f9a5f9de097babebb81762.pdf', 'application/pdf', 142400, 'file', '78f68e7b-ac0c-44b1-97ca-f30044b53553', NULL);
 
 
 --
@@ -453,7 +454,7 @@ INSERT INTO public.identities (id, user_id, created, updated, type, identifier,
 -- Data for Name: manuscripts; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.9+02', '2020-08-12 16:17:12.499+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"notes": [{"content": "", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions"}], "title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": "https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml", "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keyword", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
+INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.481+02', '2020-08-15 23:40:05.495+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": [{"url": "https://doi.org/10.6084/m9.figshare.913521.v1"}, {"url": "https://github.com/jure/mathtype_to_mathml"}], "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keywords", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
 
 
 --
@@ -499,20 +500,20 @@ INSERT INTO public.migrations (id, run_at) VALUES ('1596838897-files.sql', '2020
 -- Data for Name: team_members; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('6505126d-2ecd-498b-a27d-18eafbdbc8a6', '2020-08-12 16:16:44.923+02', '2020-08-12 16:16:44.923+02', NULL, 'c31e3116-6176-45b5-b52c-6f7cbfc86007', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('031ae180-e94e-458d-86b7-f2dc1d456e2e', '2020-08-12 16:17:14.795+02', '2020-08-12 16:17:14.795+02', NULL, 'bf800912-56c4-44eb-b425-64e51a9824fe', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('c4ac4fc0-3fae-43b0-89ee-bf7f8fd9885f', '2020-08-12 16:17:43.262+02', '2020-08-12 16:17:43.262+02', 'invited', '0719d132-bb6c-49d9-9122-7911a48cfd60', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('69e896c6-0fdb-421d-a638-1ef7670c03c9', '2020-08-12 16:17:43.884+02', '2020-08-12 16:17:43.884+02', 'invited', '0719d132-bb6c-49d9-9122-7911a48cfd60', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('12c33ef5-4fc3-4d48-8309-a90c0461113c', '2020-08-12 16:17:44.547+02', '2020-08-12 16:17:44.547+02', 'invited', '0719d132-bb6c-49d9-9122-7911a48cfd60', '85e1300e-003c-4e96-987b-23812f902477', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('21c3e56c-c99b-4427-8789-e9d1120b64dc', '2020-08-15 23:39:31.49+02', '2020-08-15 23:39:31.49+02', NULL, '55e9d201-74a0-407f-b8e4-49da8c96ea85', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('5c51df7a-df3a-4d01-b778-883e2bf77420', '2020-08-15 23:40:09.007+02', '2020-08-15 23:40:09.007+02', NULL, '3148b252-255f-4bb7-b13e-6a0c8b02f2c7', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('ef82f708-f20c-48b2-81b7-a82b5bc9365f', '2020-08-15 23:40:39.581+02', '2020-08-15 23:40:39.581+02', 'invited', 'b9bd63d9-1dd6-413c-ba6b-35600505c239', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('26660f93-cb5a-4050-b31e-4068ee72614b', '2020-08-15 23:40:40.42+02', '2020-08-15 23:40:40.42+02', 'invited', 'b9bd63d9-1dd6-413c-ba6b-35600505c239', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('5e98ba3c-730d-4aa5-a2d7-4e95454dfd14', '2020-08-15 23:40:41.565+02', '2020-08-15 23:40:41.565+02', 'invited', 'b9bd63d9-1dd6-413c-ba6b-35600505c239', '85e1300e-003c-4e96-987b-23812f902477', NULL);
 
 
 --
 -- Data for Name: teams; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('c31e3116-6176-45b5-b52c-6f7cbfc86007', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Author', 'author', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
-INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('bf800912-56c4-44eb-b425-64e51a9824fe', '2020-08-12 16:17:14.789+02', '2020-08-12 16:17:14.789+02', 'Senior Editor', 'seniorEditor', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
-INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('0719d132-bb6c-49d9-9122-7911a48cfd60', '2020-08-12 16:17:43.258+02', '2020-08-12 16:17:43.258+02', 'Reviewers', 'reviewer', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('55e9d201-74a0-407f-b8e4-49da8c96ea85', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Author', 'author', NULL, NULL, NULL, 'team', '78f68e7b-ac0c-44b1-97ca-f30044b53553');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('3148b252-255f-4bb7-b13e-6a0c8b02f2c7', '2020-08-15 23:40:09.001+02', '2020-08-15 23:40:09.001+02', 'Senior Editor', 'seniorEditor', NULL, NULL, NULL, 'team', '78f68e7b-ac0c-44b1-97ca-f30044b53553');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('b9bd63d9-1dd6-413c-ba6b-35600505c239', '2020-08-15 23:40:39.574+02', '2020-08-15 23:40:39.574+02', 'Reviewers', 'reviewer', NULL, NULL, NULL, 'team', '78f68e7b-ac0c-44b1-97ca-f30044b53553');
 
 
 --
@@ -523,9 +524,9 @@ INSERT INTO public.users (id, created, updated, admin, email, username, password
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.973+02', '2020-07-24 16:43:43.943+02', NULL, NULL, '0000000159567341', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser4.jpg', true);
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.597+02', '2020-07-24 16:43:54.939+02', NULL, NULL, '000000032536230X', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser5.jpg', false);
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-07-24 16:44:59.306+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', true);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-12 16:17:13.598+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-08-12 16:17:14.868+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-08-12 16:17:41.916+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-15 23:40:06.938+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-08-15 23:40:09.104+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-08-15 23:40:37.796+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
 
 
 --
diff --git a/cypress/dumps/senior_editor_assigned.sql b/cypress/dumps/senior_editor_assigned.sql
index 31e0f3e7492017a2e62177a8d3ce55f58638e59f..9027d3219dda4fa185f009ac937de7f4c4a138f7 100644
--- a/cypress/dumps/senior_editor_assigned.sql
+++ b/cypress/dumps/senior_editor_assigned.sql
@@ -420,8 +420,8 @@ INSERT INTO pgboss.version (version) VALUES ('11');
 -- Data for Name: channels; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('8be9c91e-10f8-42c1-9d98-429ffb7164a0', 'a696348d-b226-4703-aad1-ff64ddf30d17', '2020-08-12 16:35:01.376+02', '2020-08-12 16:35:01.376+02', 'Manuscript discussion', 'all');
-INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('89848abe-a992-45e4-ace7-36e9c64c4162', 'a696348d-b226-4703-aad1-ff64ddf30d17', '2020-08-12 16:35:01.376+02', '2020-08-12 16:35:01.376+02', 'Editorial discussion', 'editorial');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('6894e14e-1e09-47b5-90d0-ce0e6574cca3', '78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Manuscript discussion', 'all');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('48285109-8d43-4443-aa0f-be6e16dec11e', '78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Editorial discussion', 'editorial');
 
 
 --
@@ -434,7 +434,7 @@ INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) V
 -- Data for Name: files; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.files (id, created, updated, label, file_type, filename, url, mime_type, size, type, manuscript_id, review_comment_id) VALUES ('66070eaf-6ee1-475f-98ba-774843f1238e', '2020-08-12 16:35:19.664+02', '2020-08-12 16:35:19.664+02', NULL, 'supplementary', 'test-pdf.pdf', '/static/uploads/bfc74d5e3c0b8bf97cd5a522bfdb3c87.pdf', 'application/pdf', 142400, 'file', 'a696348d-b226-4703-aad1-ff64ddf30d17', NULL);
+INSERT INTO public.files (id, created, updated, label, file_type, filename, url, mime_type, size, type, manuscript_id, review_comment_id) VALUES ('eee84304-527d-426f-a083-4cbac8d5f102', '2020-08-15 23:39:54.916+02', '2020-08-15 23:39:54.916+02', NULL, 'supplementary', 'test-pdf.pdf', '/static/uploads/b251794b90f9a5f9de097babebb81762.pdf', 'application/pdf', 142400, 'file', '78f68e7b-ac0c-44b1-97ca-f30044b53553', NULL);
 
 
 --
@@ -454,7 +454,7 @@ INSERT INTO public.identities (id, user_id, created, updated, type, identifier,
 -- Data for Name: manuscripts; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('a696348d-b226-4703-aad1-ff64ddf30d17', '2020-08-12 16:35:01.371+02', '2020-08-12 16:35:28.918+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"notes": [{"content": "", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions"}], "title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": "https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml", "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keywords", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
+INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.481+02', '2020-08-15 23:40:05.495+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": [{"url": "https://doi.org/10.6084/m9.figshare.913521.v1"}, {"url": "https://github.com/jure/mathtype_to_mathml"}], "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keywords", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
 
 
 --
@@ -500,16 +500,16 @@ INSERT INTO public.migrations (id, run_at) VALUES ('1596838897-files.sql', '2020
 -- Data for Name: team_members; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('d80380a6-c741-4e3c-9933-126cf368c43b', '2020-08-12 16:35:01.381+02', '2020-08-12 16:35:01.381+02', NULL, 'd5dc5913-db33-4576-a129-3c90321464f6', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('a58d3e82-54b7-4946-8ff8-116d84cbc065', '2020-08-12 16:36:01.535+02', '2020-08-12 16:36:01.535+02', NULL, 'd7cd46ee-8d79-47c1-9c06-da5dfd464075', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('21c3e56c-c99b-4427-8789-e9d1120b64dc', '2020-08-15 23:39:31.49+02', '2020-08-15 23:39:31.49+02', NULL, '55e9d201-74a0-407f-b8e4-49da8c96ea85', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('5c51df7a-df3a-4d01-b778-883e2bf77420', '2020-08-15 23:40:09.007+02', '2020-08-15 23:40:09.007+02', NULL, '3148b252-255f-4bb7-b13e-6a0c8b02f2c7', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
 
 
 --
 -- Data for Name: teams; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('d5dc5913-db33-4576-a129-3c90321464f6', '2020-08-12 16:35:01.376+02', '2020-08-12 16:35:01.376+02', 'Author', 'author', NULL, NULL, NULL, 'team', 'a696348d-b226-4703-aad1-ff64ddf30d17');
-INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('d7cd46ee-8d79-47c1-9c06-da5dfd464075', '2020-08-12 16:36:01.513+02', '2020-08-12 16:36:01.513+02', 'Senior Editor', 'seniorEditor', NULL, NULL, NULL, 'team', 'a696348d-b226-4703-aad1-ff64ddf30d17');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('55e9d201-74a0-407f-b8e4-49da8c96ea85', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Author', 'author', NULL, NULL, NULL, 'team', '78f68e7b-ac0c-44b1-97ca-f30044b53553');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('3148b252-255f-4bb7-b13e-6a0c8b02f2c7', '2020-08-15 23:40:09.001+02', '2020-08-15 23:40:09.001+02', 'Senior Editor', 'seniorEditor', NULL, NULL, NULL, 'team', '78f68e7b-ac0c-44b1-97ca-f30044b53553');
 
 
 --
@@ -520,9 +520,9 @@ INSERT INTO public.users (id, created, updated, admin, email, username, password
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.973+02', '2020-07-24 16:43:43.943+02', NULL, NULL, '0000000159567341', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser4.jpg', true);
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.597+02', '2020-07-24 16:43:54.939+02', NULL, NULL, '000000032536230X', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser5.jpg', false);
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-07-24 16:44:59.306+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', true);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-12 16:35:01.383+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', true);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-08-12 16:36:01.679+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-08-12 16:36:02.11+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-15 23:40:06.938+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-08-15 23:40:09.104+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-08-15 23:40:09.417+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
 
 
 --
diff --git a/cypress/dumps/submission_complete.sql b/cypress/dumps/submission_complete.sql
index f9966719d506445849d95faa2ceef2eec6ce1069..27562ea0ef1fb6d95f64f00e740268b7e341111d 100644
--- a/cypress/dumps/submission_complete.sql
+++ b/cypress/dumps/submission_complete.sql
@@ -420,8 +420,8 @@ INSERT INTO pgboss.version (version) VALUES ('11');
 -- Data for Name: channels; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('8be9c91e-10f8-42c1-9d98-429ffb7164a0', 'a696348d-b226-4703-aad1-ff64ddf30d17', '2020-08-12 16:35:01.376+02', '2020-08-12 16:35:01.376+02', 'Manuscript discussion', 'all');
-INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('89848abe-a992-45e4-ace7-36e9c64c4162', 'a696348d-b226-4703-aad1-ff64ddf30d17', '2020-08-12 16:35:01.376+02', '2020-08-12 16:35:01.376+02', 'Editorial discussion', 'editorial');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('6894e14e-1e09-47b5-90d0-ce0e6574cca3', '78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Manuscript discussion', 'all');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('48285109-8d43-4443-aa0f-be6e16dec11e', '78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Editorial discussion', 'editorial');
 
 
 --
@@ -434,7 +434,7 @@ INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) V
 -- Data for Name: files; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.files (id, created, updated, label, file_type, filename, url, mime_type, size, type, manuscript_id, review_comment_id) VALUES ('66070eaf-6ee1-475f-98ba-774843f1238e', '2020-08-12 16:35:19.664+02', '2020-08-12 16:35:19.664+02', NULL, 'supplementary', 'test-pdf.pdf', '/static/uploads/bfc74d5e3c0b8bf97cd5a522bfdb3c87.pdf', 'application/pdf', 142400, 'file', 'a696348d-b226-4703-aad1-ff64ddf30d17', NULL);
+INSERT INTO public.files (id, created, updated, label, file_type, filename, url, mime_type, size, type, manuscript_id, review_comment_id) VALUES ('eee84304-527d-426f-a083-4cbac8d5f102', '2020-08-15 23:39:54.916+02', '2020-08-15 23:39:54.916+02', NULL, 'supplementary', 'test-pdf.pdf', '/static/uploads/b251794b90f9a5f9de097babebb81762.pdf', 'application/pdf', 142400, 'file', '78f68e7b-ac0c-44b1-97ca-f30044b53553', NULL);
 
 
 --
@@ -454,7 +454,7 @@ INSERT INTO public.identities (id, user_id, created, updated, type, identifier,
 -- Data for Name: manuscripts; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('a696348d-b226-4703-aad1-ff64ddf30d17', '2020-08-12 16:35:01.371+02', '2020-08-12 16:35:28.918+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"notes": [{"content": "", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions"}], "title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": "https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml", "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keywords", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
+INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.481+02', '2020-08-15 23:40:05.495+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": [{"url": "https://doi.org/10.6084/m9.figshare.913521.v1"}, {"url": "https://github.com/jure/mathtype_to_mathml"}], "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keywords", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
 
 
 --
@@ -500,14 +500,14 @@ INSERT INTO public.migrations (id, run_at) VALUES ('1596838897-files.sql', '2020
 -- Data for Name: team_members; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('d80380a6-c741-4e3c-9933-126cf368c43b', '2020-08-12 16:35:01.381+02', '2020-08-12 16:35:01.381+02', NULL, 'd5dc5913-db33-4576-a129-3c90321464f6', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('21c3e56c-c99b-4427-8789-e9d1120b64dc', '2020-08-15 23:39:31.49+02', '2020-08-15 23:39:31.49+02', NULL, '55e9d201-74a0-407f-b8e4-49da8c96ea85', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
 
 
 --
 -- Data for Name: teams; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('d5dc5913-db33-4576-a129-3c90321464f6', '2020-08-12 16:35:01.376+02', '2020-08-12 16:35:01.376+02', 'Author', 'author', NULL, NULL, NULL, 'team', 'a696348d-b226-4703-aad1-ff64ddf30d17');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('55e9d201-74a0-407f-b8e4-49da8c96ea85', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Author', 'author', NULL, NULL, NULL, 'team', '78f68e7b-ac0c-44b1-97ca-f30044b53553');
 
 
 --
@@ -520,7 +520,7 @@ INSERT INTO public.users (id, created, updated, admin, email, username, password
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.597+02', '2020-07-24 16:43:54.939+02', NULL, NULL, '000000032536230X', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser5.jpg', false);
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-07-24 16:49:06.488+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', true);
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-07-24 16:44:59.306+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', true);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-12 16:35:01.383+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', true);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-15 23:39:31.533+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', true);
 
 
 --
diff --git a/cypress/dumps/three_reviews_completed.sql b/cypress/dumps/three_reviews_completed.sql
index 8e7c8bba5d5ed812fb202665671a3580c2784c6c..5c1b2a52845b5c1d962ba1607b1ee035c51395bf 100644
--- a/cypress/dumps/three_reviews_completed.sql
+++ b/cypress/dumps/three_reviews_completed.sql
@@ -420,8 +420,8 @@ INSERT INTO pgboss.version (version) VALUES ('11');
 -- Data for Name: channels; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('afd95c06-011b-46e3-9985-964283d51c4a', '93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Manuscript discussion', 'all');
-INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('75540565-6619-495f-a8c5-d6bf11ece72d', '93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Editorial discussion', 'editorial');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('6894e14e-1e09-47b5-90d0-ce0e6574cca3', '78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Manuscript discussion', 'all');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('48285109-8d43-4443-aa0f-be6e16dec11e', '78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Editorial discussion', 'editorial');
 
 
 --
@@ -434,6 +434,7 @@ INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) V
 -- Data for Name: files; Type: TABLE DATA; Schema: public; Owner: test
 --
 
+INSERT INTO public.files (id, created, updated, label, file_type, filename, url, mime_type, size, type, manuscript_id, review_comment_id) VALUES ('eee84304-527d-426f-a083-4cbac8d5f102', '2020-08-15 23:39:54.916+02', '2020-08-15 23:39:54.916+02', NULL, 'supplementary', 'test-pdf.pdf', '/static/uploads/b251794b90f9a5f9de097babebb81762.pdf', 'application/pdf', 142400, 'file', '78f68e7b-ac0c-44b1-97ca-f30044b53553', NULL);
 
 
 --
@@ -453,7 +454,7 @@ INSERT INTO public.identities (id, user_id, created, updated, type, identifier,
 -- Data for Name: manuscripts; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.9+02', '2020-08-12 16:17:12.499+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"notes": [{"content": "", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions"}], "title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": "https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml", "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keyword", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
+INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('78f68e7b-ac0c-44b1-97ca-f30044b53553', '2020-08-15 23:39:31.481+02', '2020-08-15 23:40:05.495+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": [{"url": "https://doi.org/10.6084/m9.figshare.913521.v1"}, {"url": "https://github.com/jure/mathtype_to_mathml"}], "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keywords", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
 
 
 --
@@ -487,41 +488,41 @@ INSERT INTO public.migrations (id, run_at) VALUES ('1596838897-files.sql', '2020
 -- Data for Name: review_comments; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('f73fdbd8-6fb6-4d7a-a74a-ab2c2c07b572', '2020-08-13 15:09:33.838+02', '2020-08-13 15:09:33.838+02', '5a06fcf3-d368-4f65-917c-1acdb73fcc71', NULL, '<p>Great paper, congratulations! Gale Davis</p>', 'review', 'ReviewComment');
-INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('5c86da62-bc7f-4d23-8968-62bd5a56a52b', '2020-08-13 15:09:34.88+02', '2020-08-13 15:09:34.88+02', '5a06fcf3-d368-4f65-917c-1acdb73fcc71', NULL, '<p>This is a very important paper. Gale Davis</p>', 'confidential', 'ReviewComment');
-INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('ef96b249-dab6-4f7e-b24e-571b340b9b41', '2020-08-13 15:09:38.761+02', '2020-08-13 15:09:38.761+02', '7c6cd2f6-c23e-4902-96ac-df4801ac3d0a', NULL, '<p>Great paper, congratulations! Sherry Crofoot</p>', 'review', 'ReviewComment');
-INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('04a8a70e-7787-4fd6-bdca-bfac3aa86951', '2020-08-13 15:09:40.155+02', '2020-08-13 15:09:40.155+02', '7c6cd2f6-c23e-4902-96ac-df4801ac3d0a', NULL, '<p>This is a very important paper. Sherry Crofoot</p>', 'confidential', 'ReviewComment');
-INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('7ccda096-07c2-4569-ad0f-5b3574db205e', '2020-08-13 15:09:44.046+02', '2020-08-13 15:09:44.046+02', '3a34e0ef-6695-4268-b901-60de20f9cf4e', NULL, '<p>Great paper, congratulations! Elaine Barnes</p>', 'review', 'ReviewComment');
-INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('c8b7d1c9-aef2-47d0-a9ad-c9d46706e79e', '2020-08-13 15:09:45.548+02', '2020-08-13 15:09:45.548+02', '3a34e0ef-6695-4268-b901-60de20f9cf4e', NULL, '<p>This is a very important paper. Elaine Barnes</p>', 'confidential', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('f27dcbeb-fa71-4037-81cb-d97f08b42e52', '2020-08-15 23:41:00.217+02', '2020-08-15 23:41:00.217+02', '8970d68a-c5ec-4e4e-bd7d-407449f7cf2c', NULL, '<p>Great paper, congratulations! Gale Davis</p>', 'review', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('24236d62-adbe-4ef1-b671-734297c47570', '2020-08-15 23:41:01.33+02', '2020-08-15 23:41:01.33+02', '8970d68a-c5ec-4e4e-bd7d-407449f7cf2c', NULL, '<p>This is a very important paper. Gale Davis</p>', 'confidential', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('fb52cc08-f7b7-4720-88e2-4536f02599c4', '2020-08-15 23:41:05.761+02', '2020-08-15 23:41:05.761+02', '394fce94-31b2-4b3d-9182-baf35759e1f6', NULL, '<p>Great paper, congratulations! Sherry Crofoot</p>', 'review', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('8adf1f76-8bcd-4967-844f-dfa9b5ae652f', '2020-08-15 23:41:07.444+02', '2020-08-15 23:41:07.444+02', '394fce94-31b2-4b3d-9182-baf35759e1f6', NULL, '<p>This is a very important paper. Sherry Crofoot</p>', 'confidential', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('e4beb5ef-0c45-4256-aa8b-2982dd851ccd', '2020-08-15 23:41:12.853+02', '2020-08-15 23:41:12.853+02', 'dcf6e734-c549-49b4-a60d-18a9473762fb', NULL, '<p>Great paper, congratulations! Elaine Barnes</p>', 'review', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('ec71b59e-baa8-4f7c-ac9e-e6365892fe8f', '2020-08-15 23:41:15.069+02', '2020-08-15 23:41:15.069+02', 'dcf6e734-c549-49b4-a60d-18a9473762fb', NULL, '<p>This is a very important paper. Elaine Barnes</p>', 'confidential', 'ReviewComment');
 
 
 --
 -- Data for Name: reviews; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('5a06fcf3-d368-4f65-917c-1acdb73fcc71', '2020-08-13 15:09:32.358+02', '2020-08-13 15:09:34.925+02', 'accepted', false, '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
-INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('7c6cd2f6-c23e-4902-96ac-df4801ac3d0a', '2020-08-13 15:09:36.501+02', '2020-08-13 15:09:40.159+02', 'accepted', false, '0da0bbec-9261-4706-b990-0c10aa3cc6b4', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
-INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('3a34e0ef-6695-4268-b901-60de20f9cf4e', '2020-08-13 15:09:41.911+02', '2020-08-13 15:09:45.561+02', 'accepted', false, '85e1300e-003c-4e96-987b-23812f902477', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('8970d68a-c5ec-4e4e-bd7d-407449f7cf2c', '2020-08-15 23:40:58.391+02', '2020-08-15 23:41:01.335+02', 'accepted', false, '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '78f68e7b-ac0c-44b1-97ca-f30044b53553', 'Review');
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('394fce94-31b2-4b3d-9182-baf35759e1f6', '2020-08-15 23:41:03.424+02', '2020-08-15 23:41:07.45+02', 'accepted', false, '0da0bbec-9261-4706-b990-0c10aa3cc6b4', '78f68e7b-ac0c-44b1-97ca-f30044b53553', 'Review');
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('dcf6e734-c549-49b4-a60d-18a9473762fb', '2020-08-15 23:41:09.784+02', '2020-08-15 23:41:15.077+02', 'accepted', false, '85e1300e-003c-4e96-987b-23812f902477', '78f68e7b-ac0c-44b1-97ca-f30044b53553', 'Review');
 
 
 --
 -- Data for Name: team_members; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('6505126d-2ecd-498b-a27d-18eafbdbc8a6', '2020-08-12 16:16:44.923+02', '2020-08-12 16:16:44.923+02', NULL, 'c31e3116-6176-45b5-b52c-6f7cbfc86007', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('031ae180-e94e-458d-86b7-f2dc1d456e2e', '2020-08-12 16:17:14.795+02', '2020-08-12 16:17:14.795+02', NULL, 'bf800912-56c4-44eb-b425-64e51a9824fe', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('c4ac4fc0-3fae-43b0-89ee-bf7f8fd9885f', '2020-08-12 16:17:43.262+02', '2020-08-13 15:09:41.898+02', 'completed', '0719d132-bb6c-49d9-9122-7911a48cfd60', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('69e896c6-0fdb-421d-a638-1ef7670c03c9', '2020-08-12 16:17:43.884+02', '2020-08-13 15:09:41.898+02', 'completed', '0719d132-bb6c-49d9-9122-7911a48cfd60', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('12c33ef5-4fc3-4d48-8309-a90c0461113c', '2020-08-12 16:17:44.547+02', '2020-08-13 15:09:45.782+02', 'completed', '0719d132-bb6c-49d9-9122-7911a48cfd60', '85e1300e-003c-4e96-987b-23812f902477', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('21c3e56c-c99b-4427-8789-e9d1120b64dc', '2020-08-15 23:39:31.49+02', '2020-08-15 23:39:31.49+02', NULL, '55e9d201-74a0-407f-b8e4-49da8c96ea85', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('5c51df7a-df3a-4d01-b778-883e2bf77420', '2020-08-15 23:40:09.007+02', '2020-08-15 23:40:09.007+02', NULL, '3148b252-255f-4bb7-b13e-6a0c8b02f2c7', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('ef82f708-f20c-48b2-81b7-a82b5bc9365f', '2020-08-15 23:40:39.581+02', '2020-08-15 23:41:09.713+02', 'completed', 'b9bd63d9-1dd6-413c-ba6b-35600505c239', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('26660f93-cb5a-4050-b31e-4068ee72614b', '2020-08-15 23:40:40.42+02', '2020-08-15 23:41:09.713+02', 'completed', 'b9bd63d9-1dd6-413c-ba6b-35600505c239', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('5e98ba3c-730d-4aa5-a2d7-4e95454dfd14', '2020-08-15 23:40:41.565+02', '2020-08-15 23:41:15.341+02', 'completed', 'b9bd63d9-1dd6-413c-ba6b-35600505c239', '85e1300e-003c-4e96-987b-23812f902477', NULL);
 
 
 --
 -- Data for Name: teams; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('c31e3116-6176-45b5-b52c-6f7cbfc86007', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Author', 'author', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
-INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('bf800912-56c4-44eb-b425-64e51a9824fe', '2020-08-12 16:17:14.789+02', '2020-08-12 16:17:14.789+02', 'Senior Editor', 'seniorEditor', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
-INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('0719d132-bb6c-49d9-9122-7911a48cfd60', '2020-08-12 16:17:43.258+02', '2020-08-13 15:09:41.898+02', 'Reviewers', 'reviewer', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('55e9d201-74a0-407f-b8e4-49da8c96ea85', '2020-08-15 23:39:31.484+02', '2020-08-15 23:39:31.484+02', 'Author', 'author', NULL, NULL, NULL, 'team', '78f68e7b-ac0c-44b1-97ca-f30044b53553');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('3148b252-255f-4bb7-b13e-6a0c8b02f2c7', '2020-08-15 23:40:09.001+02', '2020-08-15 23:40:09.001+02', 'Senior Editor', 'seniorEditor', NULL, NULL, NULL, 'team', '78f68e7b-ac0c-44b1-97ca-f30044b53553');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('b9bd63d9-1dd6-413c-ba6b-35600505c239', '2020-08-15 23:40:39.574+02', '2020-08-15 23:41:09.713+02', 'Reviewers', 'reviewer', NULL, NULL, NULL, 'team', '78f68e7b-ac0c-44b1-97ca-f30044b53553');
 
 
 --
@@ -529,12 +530,12 @@ INSERT INTO public.teams (id, created, updated, name, role, members, owners, glo
 --
 
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.597+02', '2020-07-24 16:43:54.939+02', NULL, NULL, '000000032536230X', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser5.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-12 16:17:13.598+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-08-12 16:17:14.868+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.973+02', '2020-08-13 15:09:35.913+02', NULL, NULL, '0000000159567341', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser4.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-08-13 15:09:41.198+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('85e1300e-003c-4e96-987b-23812f902477', '2020-07-21 16:35:38.381+02', '2020-08-13 15:09:46.741+02', NULL, NULL, '0000000294294446', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser1.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-08-13 15:09:47.167+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-15 23:40:06.938+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-08-15 23:40:09.104+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.973+02', '2020-08-15 23:41:02.706+02', NULL, NULL, '0000000159567341', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser4.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-08-15 23:41:08.741+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('85e1300e-003c-4e96-987b-23812f902477', '2020-07-21 16:35:38.381+02', '2020-08-15 23:41:17.176+02', NULL, NULL, '0000000294294446', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser1.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-08-15 23:41:17.663+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
 
 
 --
diff --git a/cypress/integration/formbuilder_spec.js b/cypress/integration/formbuilder_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..89ff68abbfdd36f1a556848ca1697bebbd91581f
--- /dev/null
+++ b/cypress/integration/formbuilder_spec.js
@@ -0,0 +1,10 @@
+describe('Form builder', () => {
+  it('views a form field', () => {
+    cy.task('restore', 'initialState')
+    cy.login('Sinead Sullivan') // Admin
+    cy.contains('Forms').click()
+    cy.contains('Research Object Submission Form')
+    cy.contains('Name (TextField)').click()
+    cy.get('input[name="name"]').should('have.value', 'submission.name')
+  })
+})
diff --git a/cypress/integration/submission_spec.js b/cypress/integration/submission_spec.js
index 6e98b5780ae19d36a1e7dc25051e8994f995c7ac..b43d1ea9e32044fb8eaf53f62d050ebb6980e41d 100644
--- a/cypress/integration/submission_spec.js
+++ b/cypress/integration/submission_spec.js
@@ -22,6 +22,16 @@ describe('URL submission test', () => {
       .click()
 
     cy.get('body').contains('Submission created')
+
+    cy.contains('Add a link').click()
+    cy.get('[name="submission.links.0.url"]')
+      .click()
+      .type('https://doi.org/10.6084/m9.figshare.913521.v1')
+    cy.contains('Add another link').click()
+    cy.get('[name="submission.links.1.url"]')
+      .click()
+      .type('https://github.com/jure/mathtype_to_mathml')
+
     cy.get('input[data-testid="meta.title"]')
       .click()
       .clear()
@@ -56,12 +66,6 @@ describe('URL submission test', () => {
       .click()
       .type('Erica James, Matthew Matretzky')
 
-    cy.get('[data-testid="submission.links"]')
-      .click()
-      .type(
-        'https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml',
-      )
-
     // Supplementary file upload
     cy.fixture('test-pdf.pdf', 'base64').then(fileContent => {
       cy.get('[data-testid="dropzone"]').attachFile(
@@ -159,21 +163,8 @@ describe('URL submission test', () => {
     cy.contains('Control Panel').click()
     cy.contains('This is my data and code availability statement')
     cy.contains('test-pdf.pdf')
+    cy.contains('https://github.com/jure/mathtype_to_mathml')
+    cy.contains('https://doi.org/10.6084/m9.figshare.913521.v1')
     cy.task('dump', 'senior_editor_assigned')
   })
 })
-
-// Example of a file upload
-//     2. Submit a PDF
-//     cy.fixture('test-pdf.pdf', 'base64').then(fileContent => {
-//       cy.get('[data-testid="dropzone"]').upload(
-//         {
-//           fileContent,
-//           fileName: 'test-pdf.pdf',
-//           encoding: 'base64',
-//           mimeType: 'application/pdf',
-//         },
-//         { subjectType: 'drag-n-drop' },
-//       )
-//     })
-// })
diff --git a/package.json b/package.json
index 3c2e9dd19052bbe00d0f777850575309a35973b4..1124e5a1684bff813d892269f53472a48751175b 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
 {
-  "name": "Kotahi",
+  "name": "kotahi",
   "version": "1.0.0-alpha",
   "private": true,
   "description": "Kotahi - open journals",
@@ -25,6 +25,7 @@
     "faker": "4.1.0",
     "font-awesome": "4.7.0",
     "fs-extra": "4.0.3",
+    "got": "^11.5.1",
     "graphql": "14.7.0",
     "graphql-middleware": "4.0.2",
     "graphql-shield": "7.3.4",
@@ -33,6 +34,16 @@
     "jimp": "0.16.0",
     "joi": "10.6.0",
     "loadable-components": "0.4.0",
+    "memory-cache": "^0.2.0",
+    "metascraper": "^5.13.1",
+    "metascraper-author": "^5.13.1",
+    "metascraper-date": "^5.13.1",
+    "metascraper-description": "^5.13.1",
+    "metascraper-image": "^5.13.1",
+    "metascraper-logo": "^5.13.1",
+    "metascraper-logo-favicon": "^5.13.1",
+    "metascraper-publisher": "^5.13.1",
+    "metascraper-title": "^5.13.1",
     "moment": "2.27.0",
     "passport-orcid": "0.0.4",
     "prop-types": "15.7.2",
diff --git a/server/formbuilder/src/resolvers.js b/server/formbuilder/src/resolvers.js
index c9aaf53d8b0af36c9d8074124e972fb74d699cda..9ecdfa6c2a255b159091d3c6264039e36ce36f4d 100644
--- a/server/formbuilder/src/resolvers.js
+++ b/server/formbuilder/src/resolvers.js
@@ -1,7 +1,7 @@
 const config = require('config')
 const fs = require('fs')
+const path = require('path')
 const { readFiles, mkdirp } = require('./util')
-const form = require('../../../app/storage/forms/submit.json')
 
 const writeJson = (path, object) =>
   fs.writeFileSync(path, JSON.stringify(object, null, 2))
@@ -78,20 +78,20 @@ const resolvers = {
         throw new Error(err)
       }
     },
-    async updateForm(_, { form, id }, ctx) {
+    async updateForm(_, { form, formId }, ctx) {
       // DONE
       form = JSON.parse(form)
       try {
         const folderPath = `${config.get(
           'pubsweet-component-xpub-formbuilder.path',
         )}/`
-        let path = `${folderPath}${id}.json`
+        let path = `${folderPath}${formId}.json`
 
         if (fs.existsSync(path)) {
           let forms = JSON.parse(fs.readFileSync(path, 'utf8'))
           forms = Object.assign(forms, form)
           form = forms
-          if (id !== form.id) {
+          if (formId !== form.id) {
             fs.unlinkSync(path)
             path = `${folderPath}${form.id}.json`
           }
@@ -106,37 +106,36 @@ const resolvers = {
         throw new Error(err)
       }
     },
-    async updateFormElements(_, { form, formId }, ctx) {
+    async updateFormElement(_, { element, formId }, ctx) {
       // DONE
-      const { children } = JSON.parse(form)
-      try {
-        const folderPath = `${config.get(
-          'pubsweet-component-xpub-formbuilder.path',
-        )}/`
-        const path = `${folderPath}${formId}.json`
-        const forms = JSON.parse(fs.readFileSync(path, 'utf8'))
-        if (!forms.children) {
-          forms.children = [children]
-        } else if (forms.children.some(e => e.id === children.id)) {
-          const FormChildren = forms.children.map(value =>
-            value.id === children.id ? children : value,
-          )
-          forms.children = FormChildren
-        } else {
-          forms.children.push(children)
-        }
-
-        writeJson(path, forms)
-        const form = await mergeFiles(folderPath)
-        return form
-      } catch (err) {
-        throw new Error(err)
+      element = JSON.parse(element)
+
+      const folderPath = `${config.get(
+        'pubsweet-component-xpub-formbuilder.path',
+      )}/`
+      const path = `${folderPath}${formId}.json`
+      const form = JSON.parse(fs.readFileSync(path, 'utf8'))
+      if (!form.children) {
+        form.children = [element]
+      } else if (form.children.some(e => e.id === element.id)) {
+        form.children = form.children.map(value =>
+          value.id === element.id ? element : value,
+        )
+      } else {
+        form.children.push(element)
       }
+
+      writeJson(path, form)
+      return mergeFiles(folderPath)
     },
   },
   Query: {
     async getFile() {
-      return form
+      return JSON.parse(
+        fs.readFileSync(
+          path.join(__dirname, '../../../app/storage/forms/submit.json'),
+        ),
+      )
     },
     async getForms() {
       try {
diff --git a/server/formbuilder/src/typeDefs.js b/server/formbuilder/src/typeDefs.js
index 3b16f2b038507494746837d0363b45ed660a3b64..11d8b0d27c30b6ff414088b3129524c9c550e88a 100644
--- a/server/formbuilder/src/typeDefs.js
+++ b/server/formbuilder/src/typeDefs.js
@@ -8,7 +8,7 @@ const typeDefs = `
   extend type Mutation {
     createForm(form: String!): Form
     updateForm(form: String!, id: String!): Form
-    updateFormElements(form: String!, formId: String!): Form
+    updateFormElement(element: String!, formId: String!): Form
     deleteFormElement(formId: ID!, elementId: ID!): Form
     deleteForms(formId: ID!): Form
   }
diff --git a/server/model-manuscript/src/detailsForURLResolver.js b/server/model-manuscript/src/detailsForURLResolver.js
new file mode 100644
index 0000000000000000000000000000000000000000..d9bde6e3eeb3123358a4d650eed11f3d0765a4b0
--- /dev/null
+++ b/server/model-manuscript/src/detailsForURLResolver.js
@@ -0,0 +1,30 @@
+const got = require('got')
+const cache = require('memory-cache')
+
+const metascraper = require('metascraper')([
+  require('metascraper-author')(),
+  require('metascraper-date')(),
+  require('metascraper-description')(),
+  require('metascraper-image')(),
+  require('metascraper-logo')(),
+  require('metascraper-logo-favicon')(),
+  require('metascraper-publisher')(),
+  require('metascraper-title')(),
+])
+
+const detailsForURLResolver = async url => {
+  const cachedResult = cache.get(url)
+  if (cachedResult) return cachedResult
+
+  let data
+  try {
+    const { body: html } = await got(url)
+    data = await metascraper({ url, html })
+  } catch (err) {
+    data = {}
+  }
+  cache.put(url, data, 24 * 60 * 60 * 1000)
+  return data
+}
+
+module.exports = detailsForURLResolver
diff --git a/server/model-manuscript/src/graphql.js b/server/model-manuscript/src/graphql.js
index c3819736ac1427bc73f031ed0e1e5b967dadfdb5..963401ccbffd4f142d9df217ca9d4fe6e0d97c45 100644
--- a/server/model-manuscript/src/graphql.js
+++ b/server/model-manuscript/src/graphql.js
@@ -1,5 +1,5 @@
-const merge = require('lodash/merge')
-const form = require('../../../app/storage/forms/submit.json')
+// const merge = require('lodash/merge')
+const detailsForURLResolver = require('./detailsForURLResolver')
 const { ref } = require('objection')
 
 const resolvers = {
@@ -125,7 +125,16 @@ const resolvers = {
     async updateManuscript(_, { id, input }, ctx) {
       const data = JSON.parse(input)
       const manuscript = await ctx.models.Manuscript.query().findById(id)
-      const update = merge({}, manuscript, data)
+      const update = Object.assign({}, manuscript, data)
+      // We specifically merge submission, as it itself has nested properties
+      // But we don't want to do a deep merge unconditionally, as that prevents
+      // any kind of deletion happening.
+      update.submission = Object.assign(
+        {},
+        manuscript.submission,
+        data.submission,
+      )
+      // const update.submission =
       return ctx.models.Manuscript.query().updateAndFetchById(id, update)
     },
     async makeDecision(_, { id, decision }, ctx) {
@@ -259,8 +268,8 @@ const resolvers = {
 
       // return ctx.connectors.User.fetchAll(where, ctx, { eager })
     },
-    async getFile() {
-      return form
+    async detailsForURL(_, { url }) {
+      return detailsForURLResolver(url)
     },
   },
   // We want submission into to come out as a stringified JSON, so that we don't have to
@@ -301,6 +310,17 @@ const typeDefs = `
     manuscript(id: ID!): Manuscript!
     manuscripts: [Manuscript]!
     paginatedManuscripts(sort: String, offset: Int, limit: Int, filter: ManuscriptsFilter): PaginatedManuscripts
+    detailsForURL(url: String!): URLMetadata
+  }
+
+  type URLMetadata {
+    title: String
+    author: String
+    date: String
+    description: String
+    image: String
+    logo: String
+    publisher: String
   }
 
   input ManuscriptsFilter {
diff --git a/yarn.lock b/yarn.lock
index 687b143ec637004a221826d8413a735b56893414..47195b678d3d5f52a517316f57b22d88e596dbea 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1827,6 +1827,32 @@
     "@babel/runtime" "^7.7.2"
     regenerator-runtime "^0.13.3"
 
+"@metascraper/helpers@^5.13.1":
+  version "5.13.1"
+  resolved "https://registry.yarnpkg.com/@metascraper/helpers/-/helpers-5.13.1.tgz#427cce18b57e316d442f30ef96ae5cd20884233d"
+  integrity sha512-iGnjUlkTUWn5czMVLRNR+rjVJpLv/xLR0qYyOblnjjt2AJ2r7PtVs8JIFQdYu3eGK+KeCxQCiWy79S6Ks7bonQ==
+  dependencies:
+    audio-extensions "0.0.0"
+    chrono-node "2.1.5"
+    condense-whitespace "~2.0.0"
+    entities "~2.0.3"
+    file-extension "~4.0.5"
+    has-values "~2.0.1"
+    image-extensions "~1.1.0"
+    is-relative-url "~3.0.0"
+    is-uri "~1.2.0"
+    iso-639-3 "~2.1.0"
+    isostring "0.0.1"
+    lodash "~4.17.19"
+    memoize-one "~5.1.1"
+    mime-types "~2.1.27"
+    normalize-url "~5.0.0"
+    smartquotes "~2.3.1"
+    title "~3.4.2"
+    truncate "~2.1.0"
+    url-regex "~5.0.0"
+    video-extensions "~1.1.0"
+
 "@mrmlnc/readdir-enhanced@^2.2.1":
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -2022,6 +2048,18 @@
   dependencies:
     any-observable "^0.3.0"
 
+"@sindresorhus/is@^3.0.0":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-3.1.0.tgz#d8735532635bea69ad39119df5f0f10099bd09dc"
+  integrity sha512-n4J+zu52VdY43kdi/XdI9DzuMr1Mur8zFL5ZRG2opCans9aiFwkPxHYFEb5Xgy7n1Z4K6WfI4FpqUqsh3E8BPQ==
+
+"@szmarczak/http-timer@^4.0.5":
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152"
+  integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==
+  dependencies:
+    defer-to-connect "^2.0.0"
+
 "@types/accepts@*", "@types/accepts@^1.3.5":
   version "1.3.5"
   resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575"
@@ -2042,6 +2080,16 @@
     "@types/connect" "*"
     "@types/node" "*"
 
+"@types/cacheable-request@^6.0.1":
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976"
+  integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==
+  dependencies:
+    "@types/http-cache-semantics" "*"
+    "@types/keyv" "*"
+    "@types/node" "*"
+    "@types/responselike" "*"
+
 "@types/color-name@^1.1.1":
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
@@ -2135,6 +2183,11 @@
   resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b"
   integrity sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==
 
+"@types/http-cache-semantics@*":
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a"
+  integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==
+
 "@types/json-schema@^7.0.4":
   version "7.0.4"
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
@@ -2150,6 +2203,13 @@
   resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72"
   integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==
 
+"@types/keyv@*":
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7"
+  integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==
+  dependencies:
+    "@types/node" "*"
+
 "@types/koa-compose@*":
   version "3.2.5"
   resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.5.tgz#85eb2e80ac50be95f37ccf8c407c09bbe3468e9d"
@@ -2218,6 +2278,13 @@
   resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
   integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
 
+"@types/responselike@*", "@types/responselike@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
+  integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
+  dependencies:
+    "@types/node" "*"
+
 "@types/serve-static@*":
   version "1.13.4"
   resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.4.tgz#6662a93583e5a6cabca1b23592eb91e12fa80e7c"
@@ -2673,7 +2740,7 @@ ansi-styles@^2.2.1:
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
   integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
 
-ansi-styles@^3.2.0, ansi-styles@^3.2.1:
+ansi-styles@^3.1.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
   integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
@@ -2994,7 +3061,7 @@ aproba@^1.0.3, aproba@^1.1.1:
   resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
   integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
 
-arch@^2.1.2:
+arch@^2.1.0, arch@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.2.tgz#0c52bbe7344bb4fa260c443d2cbad9c00ff2f0bf"
   integrity sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==
@@ -3007,6 +3074,11 @@ are-we-there-yet@~1.1.2:
     delegates "^1.0.0"
     readable-stream "^2.0.6"
 
+arg@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/arg/-/arg-1.0.0.tgz#444d885a4e25b121640b55155ef7cd03975d6050"
+  integrity sha512-Wk7TEzl1KqvTGs/uyhmHO/3XLd3t1UeU4IstvPXVzGPM522cTjqjNZ99esCkcL52sjqjo8e8CTBcWhkxvGzoAw==
+
 argparse@^1.0.7:
   version "1.0.10"
   resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@@ -3258,6 +3330,11 @@ attr-accept@^2.0.0:
   resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.1.0.tgz#a231a854385d36ff7a99647bb77b33c8a5175aee"
   integrity sha512-sLzVM3zCCmmDtDNhI0i96k6PUztkotSOXqE4kDGQt/6iDi5M+H0srjeF+QC6jN581l4X/Zq3Zu/tgcErEssavg==
 
+audio-extensions@0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/audio-extensions/-/audio-extensions-0.0.0.tgz#d0eefe077fb9eb625898eed9985890548cf1f8d2"
+  integrity sha1-0O7+B3+562JYmO7ZmFiQVIzx+NI=
+
 authsome@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/authsome/-/authsome-0.1.0.tgz#4152e666afcaa7f7c51ddbed94145ad7105904d9"
@@ -4301,6 +4378,24 @@ cache-base@^1.0.1:
     union-value "^1.0.0"
     unset-value "^1.0.0"
 
+cacheable-lookup@^5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz#049fdc59dffdd4fc285e8f4f82936591bd59fec3"
+  integrity sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w==
+
+cacheable-request@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58"
+  integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==
+  dependencies:
+    clone-response "^1.0.2"
+    get-stream "^5.1.0"
+    http-cache-semantics "^4.0.0"
+    keyv "^4.0.0"
+    lowercase-keys "^2.0.0"
+    normalize-url "^4.1.0"
+    responselike "^2.0.0"
+
 cachedir@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8"
@@ -4426,6 +4521,15 @@ ccount@^1.0.0:
   resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.5.tgz#ac82a944905a65ce204eb03023157edf29425c17"
   integrity sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==
 
+chalk@2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba"
+  integrity sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==
+  dependencies:
+    ansi-styles "^3.1.0"
+    escape-string-regexp "^1.0.5"
+    supports-color "^4.0.0"
+
 chalk@^1.0.0, chalk@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@@ -4499,7 +4603,12 @@ check-types@^8.0.3:
   resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
   integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==
 
-cheerio@^1.0.0-rc.3:
+cheerio-advanced-selectors@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/cheerio-advanced-selectors/-/cheerio-advanced-selectors-2.0.1.tgz#fb5ec70a4599e8cec1cf669c6d9b90a3fa969c48"
+  integrity sha512-5wHR8bpiD5pdUtaS81A6hnJezzoDzL1TLWfK6bxnLkIgEKPV26BlOdMCcvuj3fTE7JSalsTUeNU7AOD/u6bYhw==
+
+cheerio@^1.0.0-rc.3, cheerio@~1.0.0-rc.3:
   version "1.0.0-rc.3"
   resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6"
   integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==
@@ -4573,6 +4682,13 @@ chrome-trace-event@^1.0.2:
   dependencies:
     tslib "^1.9.0"
 
+chrono-node@2.1.5:
+  version "2.1.5"
+  resolved "https://registry.yarnpkg.com/chrono-node/-/chrono-node-2.1.5.tgz#b691b0e925d75f0c9ebff518673af6543cc3b2d9"
+  integrity sha512-KshuspQwOhVcZ/8cY11p2+230qIX8VE9P7W8LdwSMWbMwu3TKl6BcHLccmkCt4m+QUK23yuOMD61yPXmjC++bg==
+  dependencies:
+    dayjs "^1.8.29"
+
 ci-info@^1.5.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497"
@@ -4625,7 +4741,7 @@ clean-css@4.2.x:
   dependencies:
     source-map "~0.6.0"
 
-clean-stack@^2.0.0:
+clean-stack@^2.0.0, clean-stack@~2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
   integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
@@ -4698,6 +4814,14 @@ cliff@0.1.9:
     eyes "0.1.x"
     winston "0.8.x"
 
+clipboardy@1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.2.tgz#2ce320b9ed9be1514f79878b53ff9765420903e2"
+  integrity sha512-16KrBOV7bHmHdxcQiCvfUFYVFyEah4FI8vYT1Fr7CGSA4G+xBWMEfUEQJS1hxeHGtI9ju1Bzs9uXSbj5HZKArw==
+  dependencies:
+    arch "^2.1.0"
+    execa "^0.8.0"
+
 cliui@^4.0.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
@@ -4734,6 +4858,13 @@ clone-regexp@^1.0.0:
     is-regexp "^1.0.0"
     is-supported-regexp-flag "^1.0.0"
 
+clone-response@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
+  integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
+  dependencies:
+    mimic-response "^1.0.0"
+
 clone@^1.0.2:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
@@ -4969,6 +5100,11 @@ concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.5.2, concat-stream@
     readable-stream "^2.2.2"
     typedarray "^0.0.6"
 
+condense-whitespace@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/condense-whitespace/-/condense-whitespace-2.0.0.tgz#94e9644938f66aa7be4b8849f8f0b3cec97d6b3a"
+  integrity sha512-Ath9o58/0rxZXbyoy3zZgrVMoIemi30sukG/btuMKCLyqfQt3dNOWc9N3EHEMa2Q3i0tXQPDJluYFLwy7pJuQw==
+
 config@3.3.1, config@^3.0.1:
   version "3.3.1"
   resolved "https://registry.yarnpkg.com/config/-/config-3.3.1.tgz#b6a70e2908a43b98ed20be7e367edf0cc8ed5a19"
@@ -5507,6 +5643,11 @@ dateformat@^3.0.3:
   resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
   integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
 
+dayjs@^1.8.29:
+  version "1.8.31"
+  resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.31.tgz#0cd1114c2539dd5ad9428be0c38df6d4bb40b9d3"
+  integrity sha512-mPh1mslned+5PuIuiUfbw4CikHk6AEAf2Baxih+wP5fssv+wmlVhvgZ7mq+BhLt7Sr/Hc8leWDiwe6YnrpNt3g==
+
 db-errors@^0.2.3:
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/db-errors/-/db-errors-0.2.3.tgz#a6a38952e00b20e790f2695a6446b3c65497ffa2"
@@ -5563,6 +5704,13 @@ decode-uri-component@^0.2.0:
   resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
   integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
 
+decompress-response@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
+  integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
+  dependencies:
+    mimic-response "^3.1.0"
+
 dedent@^0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
@@ -5640,6 +5788,11 @@ default-require-extensions@^1.0.0:
   dependencies:
     strip-bom "^2.0.0"
 
+defer-to-connect@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.0.tgz#83d6b199db041593ac84d781b5222308ccf4c2c1"
+  integrity sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==
+
 deferred-leveldown@~5.3.0:
   version "5.3.0"
   resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058"
@@ -6113,7 +6266,7 @@ entities@^1.1.1, entities@~1.1.1:
   resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
   integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
 
-entities@^2.0.0:
+entities@^2.0.0, entities@~2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
   integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
@@ -6639,6 +6792,19 @@ execa@^0.7.0:
     signal-exit "^3.0.0"
     strip-eof "^1.0.0"
 
+execa@^0.8.0:
+  version "0.8.0"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da"
+  integrity sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=
+  dependencies:
+    cross-spawn "^5.0.1"
+    get-stream "^3.0.0"
+    is-stream "^1.1.0"
+    npm-run-path "^2.0.0"
+    p-finally "^1.0.0"
+    signal-exit "^3.0.0"
+    strip-eof "^1.0.0"
+
 execa@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
@@ -7017,6 +7183,11 @@ file-entry-cache@^4.0.0:
   dependencies:
     flat-cache "^2.0.1"
 
+file-extension@~4.0.5:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/file-extension/-/file-extension-4.0.5.tgz#ae6cef34c28e7313a92baa4aa955755cacdf0ce3"
+  integrity sha512-l0rOL3aKkoi6ea7MNZe6OHgqYYpn48Qfflr8Pe9G9JPPTx5A+sfboK91ZufzIs59/lPqh351l0eb6iKU9J5oGg==
+
 file-loader@1.1.11:
   version "1.1.11"
   resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-1.1.11.tgz#6fe886449b0f2a936e43cabaac0cdbfb369506f8"
@@ -7521,7 +7692,7 @@ get-stream@^4.0.0:
   dependencies:
     pump "^3.0.0"
 
-get-stream@^5.0.0:
+get-stream@^5.0.0, get-stream@^5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9"
   integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==
@@ -7730,6 +7901,23 @@ gonzales-pe@^4.0.3, gonzales-pe@^4.2.3:
   dependencies:
     minimist "^1.2.5"
 
+got@^11.5.1, got@~11.5.1:
+  version "11.5.1"
+  resolved "https://registry.yarnpkg.com/got/-/got-11.5.1.tgz#bf098a270fe80b3fb88ffd5a043a59ebb0a391db"
+  integrity sha512-reQEZcEBMTGnujmQ+Wm97mJs/OK6INtO6HmLI+xt3+9CvnRwWjXutUvb2mqr+Ao4Lu05Rx6+udx9sOQAmExMxA==
+  dependencies:
+    "@sindresorhus/is" "^3.0.0"
+    "@szmarczak/http-timer" "^4.0.5"
+    "@types/cacheable-request" "^6.0.1"
+    "@types/responselike" "^1.0.0"
+    cacheable-lookup "^5.0.3"
+    cacheable-request "^7.0.1"
+    decompress-response "^6.0.0"
+    http2-wrapper "^1.0.0-beta.5.0"
+    lowercase-keys "^2.0.0"
+    p-cancelable "^2.0.0"
+    responselike "^2.0.0"
+
 graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2:
   version "4.2.4"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
@@ -7877,6 +8065,11 @@ has-flag@^1.0.0:
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
   integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=
 
+has-flag@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
+  integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=
+
 has-flag@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
@@ -7928,6 +8121,13 @@ has-values@^1.0.0:
     is-number "^3.0.0"
     kind-of "^4.0.0"
 
+has-values@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/has-values/-/has-values-2.0.1.tgz#3876200ff86d8a8546a9264a952c17d5fc17579d"
+  integrity sha512-+QdH3jOmq9P8GfdjFg0eJudqx1FqU62NQJ4P16rOEHeRdl7ckgwn6uqQjzYE0ZoHVV/e5E2esuJ5Gl5+HUW19w==
+  dependencies:
+    kind-of "^6.0.2"
+
 has@^1.0.1, has@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
@@ -8173,6 +8373,11 @@ htmlparser2@^4.1.0:
     domutils "^2.0.0"
     entities "^2.0.0"
 
+http-cache-semantics@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
+  integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
+
 http-deceiver@^1.2.7:
   version "1.2.7"
   resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
@@ -8259,6 +8464,14 @@ http-status-codes@^1.0.6, http-status-codes@^1.3.0:
   resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-1.4.0.tgz#6e4c15d16ff3a9e2df03b89f3a55e1aae05fb477"
   integrity sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ==
 
+http2-wrapper@^1.0.0-beta.5.0:
+  version "1.0.0-beta.5.2"
+  resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz#8b923deb90144aea65cf834b016a340fc98556f3"
+  integrity sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==
+  dependencies:
+    quick-lru "^5.1.1"
+    resolve-alpn "^1.0.0"
+
 https-browserify@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
@@ -8351,6 +8564,11 @@ ignore@^5.0.4:
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
   integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
 
+image-extensions@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/image-extensions/-/image-extensions-1.1.0.tgz#b8e6bf6039df0056e333502a00b6637a3105d894"
+  integrity sha1-uOa/YDnfAFbjM1AqALZjejEF2JQ=
+
 image-q@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/image-q/-/image-q-1.1.1.tgz#fc84099664460b90ca862d9300b6bfbbbfbf8056"
@@ -8530,6 +8748,11 @@ ip-regex@^2.1.0:
   resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
   integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
 
+ip-regex@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.1.0.tgz#5ad62f685a14edb421abebc2fff8db94df67b455"
+  integrity sha512-pKnZpbgCTfH/1NLIlOduP/V+WRXzC2MOz3Qo8xmxk8C5GudJLgK5QyLVXOSWy3ParAH7Eemurl3xjv/WXYFvMA==
+
 ip@^1.1.0, ip@^1.1.5:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
@@ -8545,7 +8768,7 @@ is-absolute-url@^2.0.0:
   resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
   integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=
 
-is-absolute-url@^3.0.3:
+is-absolute-url@^3.0.0, is-absolute-url@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698"
   integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==
@@ -8919,6 +9142,13 @@ is-regexp@^1.0.0:
   resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
   integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk=
 
+is-relative-url@~3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-relative-url/-/is-relative-url-3.0.0.tgz#f623c8e26baa5bd3742b3b7ec074f50f3b45b3f3"
+  integrity sha512-U1iSYRlY2GIMGuZx7gezlB5dp1Kheaym7zKzO1PV06mOihiWTXejLwm4poEJysPyXF+HtK/BEd0DVlcCh30pEA==
+  dependencies:
+    is-absolute-url "^3.0.0"
+
 is-relative@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d"
@@ -8997,6 +9227,14 @@ is-unc-path@^1.0.0:
   dependencies:
     unc-path-regex "^0.1.2"
 
+is-uri@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/is-uri/-/is-uri-1.2.0.tgz#b92ff234af68c0ed97d2eed46492d01793b7d420"
+  integrity sha1-uS/yNK9owO2X0u7UZJLQF5O31CA=
+  dependencies:
+    parse-uri "~1.0.0"
+    punycode2 "~1.0.0"
+
 is-utf8@^0.2.0:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
@@ -9069,6 +9307,11 @@ isexe@^2.0.0:
   resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
   integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
 
+iso-639-3@~2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/iso-639-3/-/iso-639-3-2.1.0.tgz#f3733c7f2754c1bde34a8960b2a17146a4b41053"
+  integrity sha512-NYcq+YfrCFVGw/xWhRB9mhCSWxlOxYv3eK3WzWzc86P8huEZ7UDQq8Bu0zKqpZFOdq221Gy8VWWLr1aaYc+FJA==
+
 isobject@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
@@ -9094,6 +9337,11 @@ isomorphic.js@^0.1.3:
   resolved "https://registry.yarnpkg.com/isomorphic.js/-/isomorphic.js-0.1.4.tgz#6cc878773b0d895261a1b022e6863d564fcccd28"
   integrity sha512-t9zbgkjE7f9f2M6OSW49YEq0lUrSdAllBbWFUZoeck/rnnFae6UlhmDtXWs48VJY3ZpryCoZsRiAiKD44hPIGQ==
 
+isostring@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/isostring/-/isostring-0.0.1.tgz#ddb608efbfc89cda86db9cb16be090a788134c7f"
+  integrity sha1-3bYI77/InNqG25yxa+CQp4gTTH8=
+
 isstream@0.1.x, isstream@~0.1.2:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@@ -9585,6 +9833,11 @@ jsesc@~0.5.0:
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
   integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
 
+json-buffer@3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
+  integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
+
 json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
@@ -9717,6 +9970,13 @@ jws@^3.2.2:
     jwa "^1.4.1"
     safe-buffer "^5.0.1"
 
+keyv@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.1.tgz#9fe703cb4a94d6d11729d320af033307efd02ee6"
+  integrity sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw==
+  dependencies:
+    json-buffer "3.0.1"
+
 killable@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
@@ -10252,7 +10512,7 @@ lodash@^4, lodash@^4.17.3, lodash@^4.3.0:
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
   integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
 
-lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4:
+lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4, lodash@~4.17.19:
   version "4.17.19"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
   integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
@@ -10339,6 +10599,11 @@ lower-case@^1.1.1:
   resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
   integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
 
+lowercase-keys@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
+  integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
+
 lru-cache@^4.0.1, lru-cache@^4.1.1:
   version "4.1.5"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
@@ -10491,11 +10756,16 @@ mem@^1.1.0:
   dependencies:
     mimic-fn "^1.0.0"
 
-memoize-one@^5.0.0:
+memoize-one@^5.0.0, memoize-one@~5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
   integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==
 
+memory-cache@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/memory-cache/-/memory-cache-0.2.0.tgz#7890b01d52c00c8ebc9d533e1f8eb17e3034871a"
+  integrity sha1-eJCwHVLADI68nVM+H46xfjA0hxo=
+
 memory-fs@^0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
@@ -10576,6 +10846,77 @@ merge@^1.2.0:
   resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145"
   integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==
 
+metascraper-author@^5.13.1:
+  version "5.13.1"
+  resolved "https://registry.yarnpkg.com/metascraper-author/-/metascraper-author-5.13.1.tgz#fe00c0ffac180f45fd98b3f9d4d892a750218e58"
+  integrity sha512-vDELulSYqa+3mTXL0eMucC6paK2kMXQ2WyJNJwiNkKnK0W3+9Q8ZzGDf42WBQjbdiDbC4nJskCBINCTO+VtB8w==
+  dependencies:
+    "@metascraper/helpers" "^5.13.1"
+    lodash "~4.17.19"
+
+metascraper-date@^5.13.1:
+  version "5.13.1"
+  resolved "https://registry.yarnpkg.com/metascraper-date/-/metascraper-date-5.13.1.tgz#f17beb3fa545a595472fa484376cd9626c5b4f2d"
+  integrity sha512-xqpt//LL/TJa2N+iOFWZ99kgCLEIHX9XxQFis4L+GF5U7IalAs/nnHfkNfBU3ldiI5nKzUmG7CodzMEjYEPveA==
+  dependencies:
+    "@metascraper/helpers" "^5.13.1"
+
+metascraper-description@^5.13.1:
+  version "5.13.1"
+  resolved "https://registry.yarnpkg.com/metascraper-description/-/metascraper-description-5.13.1.tgz#04ede72de3f2d31415ae1ee231b3900b92f9a1bd"
+  integrity sha512-p0bn108z8lOyVZ0DE/MQFSIcbfLQ1AT7M6e/SHNYz6b17HJZPqlXhBO4AY85rluR6taeuMk0eyuksKSvS0BclA==
+  dependencies:
+    "@metascraper/helpers" "^5.13.1"
+
+metascraper-image@^5.13.1:
+  version "5.13.1"
+  resolved "https://registry.yarnpkg.com/metascraper-image/-/metascraper-image-5.13.1.tgz#25815f852e7695e6c3027a982ad26dfd80fb6865"
+  integrity sha512-TeDA0mdJwCNU4iFhdJXEkctunRjCaE/VGOhRFakc7I+wZYxMPJCUYd3LQUeD0Vu5dpnwJUqBepxU0kF7Z6oV5w==
+  dependencies:
+    "@metascraper/helpers" "^5.13.1"
+
+metascraper-logo-favicon@^5.13.1:
+  version "5.13.1"
+  resolved "https://registry.yarnpkg.com/metascraper-logo-favicon/-/metascraper-logo-favicon-5.13.1.tgz#28b8814669f6d402093f0d294e79497230e6539e"
+  integrity sha512-LtnbTHDsBAIRR1G3fycZqMNMLxBWAOKTcS/FVWBzVpBJrsSh/P0mE7+OqHtfeAzLMbD1OTiVev4o0b9JUp68vg==
+  dependencies:
+    "@metascraper/helpers" "^5.13.1"
+    got "~11.5.1"
+    lodash "~4.17.19"
+
+metascraper-logo@^5.13.1:
+  version "5.13.1"
+  resolved "https://registry.yarnpkg.com/metascraper-logo/-/metascraper-logo-5.13.1.tgz#0ae355fce084d5177e03a3641691eba6cb8dc981"
+  integrity sha512-RYCovxtby5jAtLnaATKpdUbXkBtZcnbMVvsR8hFDZzbXjVxBZOanqddOJp7TeKl5k1HmTuQIekV5WsN0cN9lZQ==
+  dependencies:
+    "@metascraper/helpers" "^5.13.1"
+
+metascraper-publisher@^5.13.1:
+  version "5.13.1"
+  resolved "https://registry.yarnpkg.com/metascraper-publisher/-/metascraper-publisher-5.13.1.tgz#07cbe86ec50b06291aae972daac50419af85f853"
+  integrity sha512-q1PEw81RboFHkeeFwgpG8hbyfl9CUvQcgE3UQMk5FXs+MI16qy458lxs42NGMIsSAyilX2pEsIdFxTEH0Ab22w==
+  dependencies:
+    "@metascraper/helpers" "^5.13.1"
+
+metascraper-title@^5.13.1:
+  version "5.13.1"
+  resolved "https://registry.yarnpkg.com/metascraper-title/-/metascraper-title-5.13.1.tgz#7ab51fc0763bed48f49c313ad95bf5a3f514043d"
+  integrity sha512-heWQe7p0/hwnL9DRZqRKiIdMi+y4BJj5Vo4AcI8vI2ZkugWNYkRGn3GmSrPJ0B1SRCu57/UtqOOmxPY3DCcKiQ==
+  dependencies:
+    "@metascraper/helpers" "^5.13.1"
+    lodash "~4.17.19"
+
+metascraper@^5.13.1:
+  version "5.13.1"
+  resolved "https://registry.yarnpkg.com/metascraper/-/metascraper-5.13.1.tgz#846ed788f8bbe2fd80fbfa8ac6b94fff1ee7cf83"
+  integrity sha512-1954Hxrd0p0iA8qnTlIvzFC4A+v9IRgdaeV0IMmu0LxqLXyJpIVsOlTZ3Zhw14U9PJHwHEWd9awtzSBwJkoMMQ==
+  dependencies:
+    "@metascraper/helpers" "^5.13.1"
+    cheerio "~1.0.0-rc.3"
+    cheerio-advanced-selectors "~2.0.1"
+    lodash "~4.17.19"
+    whoops "~4.1.0"
+
 methods@^1.1.1, methods@^1.1.2, methods@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@@ -10640,7 +10981,7 @@ mime-db@1.44.0, "mime-db@>= 1.43.0 < 2":
   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
   integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
 
-mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24:
+mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.27:
   version "2.1.27"
   resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
   integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
@@ -10667,6 +11008,21 @@ mimic-fn@^2.1.0:
   resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
   integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
 
+mimic-fn@~3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.0.0.tgz#76044cfa8818bbf6999c5c9acadf2d3649b14b4b"
+  integrity sha512-PiVO95TKvhiwgSwg1IdLYlCTdul38yZxZMIcnDSFIBUm4BNZha2qpQ4GpJ++15bHoKDtrW2D69lMfFwdFYtNZQ==
+
+mimic-response@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
+  integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
+
+mimic-response@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
+  integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
+
 min-document@^2.19.0:
   version "2.19.0"
   resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
@@ -11196,6 +11552,16 @@ normalize-url@1.9.1, normalize-url@^1.4.0:
     query-string "^4.1.0"
     sort-keys "^1.0.0"
 
+normalize-url@^4.1.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
+  integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
+
+normalize-url@~5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-5.0.0.tgz#f46c9dc20670495e4e18fbd1b4396e41d199f63c"
+  integrity sha512-bAEm2fx8Dq/a35Z6PIRkkBBJvR56BbEJvhpNtvCZ4W9FyORSna77fn+xtYFjqk5JpBS+fMnAOG/wFgkQBmB7hw==
+
 npm-bundled@^1.0.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b"
@@ -11569,6 +11935,11 @@ ospath@^1.2.2:
   resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b"
   integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=
 
+p-cancelable@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e"
+  integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==
+
 p-finally@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
@@ -11786,6 +12157,11 @@ parse-passwd@^1.0.0:
   resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
   integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=
 
+parse-uri@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/parse-uri/-/parse-uri-1.0.0.tgz#2872dcc22f1a797acde1583d8a0ac29552ddac20"
+  integrity sha1-KHLcwi8aeXrN4Vg9igrClVLdrCA=
+
 parse5@4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608"
@@ -13083,6 +13459,11 @@ pumpify@^1.3.3:
     inherits "^2.0.3"
     pump "^2.0.0"
 
+punycode2@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/punycode2/-/punycode2-1.0.0.tgz#e2b4b9a9a8ff157d0b84438e203181ee7892dfd8"
+  integrity sha1-4rS5qaj/FX0LhEOOIDGB7niS39g=
+
 punycode@1.3.2:
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
@@ -13151,6 +13532,11 @@ quick-lru@^1.0.0:
   resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8"
   integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=
 
+quick-lru@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
+  integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
+
 raf@^3.4.1:
   version "3.4.1"
   resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
@@ -14005,6 +14391,11 @@ requires-port@^1.0.0:
   resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
   integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
 
+resolve-alpn@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.0.0.tgz#745ad60b3d6aff4b4a48e01b8c0bdc70959e0e8c"
+  integrity sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==
+
 resolve-cwd@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
@@ -14057,6 +14448,13 @@ resolve@^1.0.0, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0
   dependencies:
     path-parse "^1.0.6"
 
+responselike@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723"
+  integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==
+  dependencies:
+    lowercase-keys "^2.0.0"
+
 restore-cursor@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
@@ -14533,6 +14931,11 @@ slice-ansi@^4.0.0:
     astral-regex "^2.0.0"
     is-fullwidth-code-point "^3.0.0"
 
+smartquotes@~2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/smartquotes/-/smartquotes-2.3.1.tgz#01ebb595d6c7a9e24d90e8cb95c17d0e1af49407"
+  integrity sha1-Aeu1ldbHqeJNkOjLlcF9Dhr0lAc=
+
 snapdragon-node@^2.0.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
@@ -15313,6 +15716,13 @@ supports-color@^3.1.2, supports-color@^3.2.3:
   dependencies:
     has-flag "^1.0.0"
 
+supports-color@^4.0.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b"
+  integrity sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=
+  dependencies:
+    has-flag "^2.0.0"
+
 supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0:
   version "5.5.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
@@ -15536,6 +15946,26 @@ tinycolor2@^1.4.1:
   resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"
   integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=
 
+title@~3.4.2:
+  version "3.4.2"
+  resolved "https://registry.yarnpkg.com/title/-/title-3.4.2.tgz#1c0acd159c6437296cc73ec743a4b9f7510c6a6f"
+  integrity sha512-cSNFZ/ChKlX2SfF+k9XvvXkjVa1JrzdGt6v/hoxVig5VaDGRmNHANfawcAdW2mfkd7y+uBHH6n9EHAxJmkO5Hw==
+  dependencies:
+    arg "1.0.0"
+    chalk "2.3.0"
+    clipboardy "1.2.2"
+    titleize "1.0.0"
+
+titleize@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/titleize/-/titleize-1.0.0.tgz#7d350722061830ba6617631e0cfd3ea08398d95a"
+  integrity sha1-fTUHIgYYMLpmF2MeDP0+oIOY2Vo=
+
+tlds@^1.203.0:
+  version "1.207.0"
+  resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.207.0.tgz#459264e644cf63ddc0965fece3898913286b1afd"
+  integrity sha512-k7d7Q1LqjtAvhtEOs3yN14EabsNO8ZCoY6RESSJDB9lst3bTx3as/m1UuAeCKzYxiyhR1qq72ZPhpSf+qlqiwg==
+
 tmp-promise@^2.0.0:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-2.1.1.tgz#eb97c038995af74efbfe8156f5e07fdd0c935539"
@@ -15678,6 +16108,11 @@ trough@^1.0.0:
   resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
   integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
 
+truncate@~2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/truncate/-/truncate-2.1.0.tgz#391183563a25cffbd4d613a1d00ae5844c9e55d3"
+  integrity sha512-em3E3SUDONOjTBcZ36DTm3RvDded3IRU9rX32oHwwXNt3rJD5MVaFlJTQvs8tJoHRoeYP36OuQ1eL/Q7bNEWIQ==
+
 tryer@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
@@ -16031,6 +16466,14 @@ url-parse@^1.4.3:
     querystringify "^2.1.1"
     requires-port "^1.0.0"
 
+url-regex@~5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/url-regex/-/url-regex-5.0.0.tgz#8f5456ab83d898d18b2f91753a702649b873273a"
+  integrity sha512-O08GjTiAFNsSlrUWfqF1jH0H1W3m35ZyadHrGv5krdnmPPoxP27oDTqux/579PtaroiSGm5yma6KT1mHFH6Y/g==
+  dependencies:
+    ip-regex "^4.1.0"
+    tlds "^1.203.0"
+
 url@^0.11.0:
   version "0.11.0"
   resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
@@ -16221,6 +16664,11 @@ vfile@^3.0.0:
     unist-util-stringify-position "^1.0.0"
     vfile-message "^1.0.0"
 
+video-extensions@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/video-extensions/-/video-extensions-1.1.0.tgz#eaa86b45f29a853c2b873e9d8e23b513712997d6"
+  integrity sha1-6qhrRfKahTwrhz6djiO1E3Epl9Y=
+
 vm-browserify@^1.0.1:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
@@ -16706,6 +17154,14 @@ which@^2.0.1:
   dependencies:
     isexe "^2.0.0"
 
+whoops@~4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/whoops/-/whoops-4.1.0.tgz#f42e51514c7af19a9491a44cabf2712292c6a8e1"
+  integrity sha512-42soctqvFs9FaU1r4ZadCy2F6A9dUc4SN3ud+tbDEdmyZDTeYBgKKqtIdo6NiQlnZnJegWRCyKLk2edYH9DsHA==
+  dependencies:
+    clean-stack "~2.2.0"
+    mimic-fn "~3.0.0"
+
 wide-align@^1.1.0:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"