From ede1b7a8dce59b0116d6f42be87e3abc172d7967 Mon Sep 17 00:00:00 2001 From: Lauren Barker Date: Thu, 17 Aug 2017 09:21:14 -0400 Subject: [PATCH 01/19] [MOD-57][Feature] Add preprint status banner (#1) * Add pending status banner to preprint detail page * Add moderator feedback dropdown * Add aria labels * Use fat arrows and objects --- .../preprint-status-banner/component.js | 99 +++++++++++++ .../preprint-status-banner/template.hbs | 42 ++++++ app/locales/en/translations.js | 19 +++ app/styles/app.scss | 136 ++++++++++++++++++ app/templates/content/index.hbs | 3 + 5 files changed, 299 insertions(+) create mode 100644 app/components/preprint-status-banner/component.js create mode 100644 app/components/preprint-status-banner/template.hbs diff --git a/app/components/preprint-status-banner/component.js b/app/components/preprint-status-banner/component.js new file mode 100644 index 000000000..f5ca1eaa9 --- /dev/null +++ b/app/components/preprint-status-banner/component.js @@ -0,0 +1,99 @@ +import Ember from 'ember'; + +const PENDING = 'pending'; +const ACCEPTED = 'accepted'; +const REJECTED = 'rejected'; + +const PRE_MODERATION = 'pre-moderation'; +const POST_MODERATION = 'post-moderation'; + +const ICONS = { + [PENDING]: 'fa-hourglass-o', + [ACCEPTED]: 'fa-check-circle-o', + [REJECTED]: 'fa-times-circle-o' +}; + +const STATUS = { + [PENDING]: 'components.preprint-status-banner.pending', + [ACCEPTED]: 'components.preprint-status-banner.accepted', + [REJECTED]: 'components.preprint-status-banner.rejected' +}; + +const MESSAGE = { + [PRE_MODERATION]: 'components.preprint-status-banner.message.pending_pre', + [POST_MODERATION]: 'components.preprint-status-banner.message.pending_post', + [ACCEPTED]: 'components.preprint-status-banner.message.accepted', + [REJECTED]: 'components.preprint-status-banner.message.rejected' +}; + +const WORKFLOW = { + [PRE_MODERATION]: 'components.preprint-status-banner.pre_moderation', + [POST_MODERATION]: 'components.preprint-status-banner.post_moderation' +}; + +const CLASS_NAMES = { + [PRE_MODERATION]: 'preprint-status-pending-pre', + [POST_MODERATION]: 'preprint-status-pending-post', + [ACCEPTED]: 'preprint-status-accepted', + [REJECTED]: 'preprint-status-rejected' +}; + + +export default Ember.Component.extend({ + theme: Ember.inject.service(), + + // translations + labelModeratorFeedback: 'components.preprint-status-banner.feedback.moderator_feedback', + moderator: 'components.preprint-status-banner.feedback.moderator', + feedbackBaseMessage: 'components.preprint-status-banner.feedback.base', + baseMessage: 'components.preprint-status-banner.message.base', + + classNames: ['preprint-status-component'], + classNameBindings: ['getClassName'], + + getClassName: Ember.computed('submission.provider.reviewsWorkflow', 'submission.reviewsState', function() { + if (this.get('submission.reviewsState') === PENDING) { + return CLASS_NAMES[this.get('submission.provider.reviewsWorkflow').toLowerCase()] + } + return CLASS_NAMES[this.get('submission.reviewsState')]; + }), + + // TODO: move to 'content/index.hbs' once null value is updated + attributeBindings: ['hidden'], + hidden: Ember.computed('submission.provider.reviewsWorkflow', function() { + return this.get('submission.provider.reviewsWorkflow') === 'none'; + }), + + didReceiveAttrs() { + this.get('submission.reviewLogs').then(reviewLogs => { + let firstLog = reviewLogs.toArray()[0]; + this.set('reviewerComment', firstLog.get('comment')); + firstLog.get('creator').then(user => { + this.set('reviewerName', user.get('fullName')); + }) + }); + }, + + reviewerComment:'', + reviewerName: '', + + bannerContent: Ember.computed('submission.provider.reviewsWorkflow', 'submission.reviewsState', function() { + if (this.get('submission.reviewsState') === PENDING) { + return MESSAGE[this.get('submission.provider.reviewsWorkflow').toLowerCase()]; + } + return MESSAGE[this.get('submission.reviewsState')]; + }), + + status: Ember.computed('submission.reviewsState', function() { + return STATUS[this.get('submission.reviewsState')]; + }), + + icon: Ember.computed('submission.reviewsState', function() { + return ICONS[this.get('submission.reviewsState')]; + }), + + workflow: Ember.computed('submission.provider.reviewsWorkflow', function () { + return WORKFLOW[this.get('submission.provider.reviewsWorkflow').toLowerCase()]; + }), + +}); diff --git a/app/components/preprint-status-banner/template.hbs b/app/components/preprint-status-banner/template.hbs new file mode 100644 index 000000000..f30aa61ae --- /dev/null +++ b/app/components/preprint-status-banner/template.hbs @@ -0,0 +1,42 @@ +
+
+ {{#if submission.provider.isPending}} + Loading... + {{else}} +
+ + {{t status}}: + + {{t baseMessage name=(if theme.isProvider theme.provider.name (t "global.brand_name")) reviewsWorkflow=(t workflow)}} {{t bannerContent}}. + +
+ {{#if (and reviewerComment (not submission.provider.reviewsCommentsPrivate))}} + + {{/if}} + {{/if}} +
+
diff --git a/app/locales/en/translations.js b/app/locales/en/translations.js index a42c4dd50..9700aa4e2 100644 --- a/app/locales/en/translations.js +++ b/app/locales/en/translations.js @@ -344,6 +344,25 @@ export default { my_projects: `My OSF Projects`, headline: `On the OSF`, }, + 'preprint-status-banner': { + message: { + base: `{{name}} uses {{reviewsWorkflow}}, therefore this preprint is`, + pending_pre: `not publicly available or searchable until approved by a moderator`, + pending_post: `publicly available and searchable but is subject to removal by a moderator`, + accepted: `publicly available and searchable`, + rejected: `not publicly available or searchable`, + }, + pending: `pending`, + accepted: `accepted`, + rejected: `rejected`, + feedback: { + moderator_feedback: `Moderator feedback`, + moderator: `Moderator`, + base: `This {{preprintWords.preprint}} is`, + }, + pre_moderation: `pre-moderation`, + post_moderation: `post-moderation`, + }, 'project-chooser': { file_upload_create: `Upload a file and create an OSF project`, provide_title: `Please provide a title for your project`, diff --git a/app/styles/app.scss b/app/styles/app.scss index eeffdfbea..509fc7511 100644 --- a/app/styles/app.scss +++ b/app/styles/app.scss @@ -337,6 +337,142 @@ $logo-dir: 'img/provider_logos/'; } } +/* Moderation */ + +$color-alert-bg-warning: #FCF8E3; +$color-alert-border-warning: #FAEBCC; +$color-alert-text-warning: #8A6D3B; + +$color-alert-bg-success: #DFF0D8; +$color-alert-border-success: #D6E9C6; +$color-alert-text-success: #3C763D; + +$color-alert-bg-danger: #F2DEDE; +$color-alert-border-danger: #EBCCD1; +$color-alert-text-danger: #A94442; + +$color-alert-bg-info: #D9EDF7; +$color-alert-border-info: #BCE8F1; +$color-alert-text-info: #31708F; + +$color-bg-color-grey: #333333; +$color-bg-color-light: #EEEEEE; +$color-border-light: #DDDDDD; + +.preprint-status-pending-pre { + background-color: $color-alert-bg-warning; + border: 1px solid $color-alert-border-warning; + color: $color-alert-text-warning; + + .status-icon { + -webkit-text-stroke: 1px $color-alert-text-warning; + font-size: 14px; + } +} + +.preprint-status-pending-post { + background-color: $color-alert-bg-info; + border: 1px solid $color-alert-border-info; + color: $color-alert-text-info; + + .status-icon { + -webkit-text-stroke: 1px $color-alert-text-info; + font-size: 14px; + } +} + +.preprint-status-accepted { + background-color: $color-alert-bg-success; + border: 1px solid $color-alert-border-success; + color: $color-alert-text-success; + + .status-icon { + -webkit-text-stroke: 0px; + font-size: 16px; + } +} + +.preprint-status-rejected { + background-color: $color-alert-bg-danger; + border: 1px solid $color-alert-border-danger; + color: $color-alert-text-danger; + + .status-icon { + -webkit-text-stroke: 0px; + font-size: 16px; + } +} + +.preprint-status-component { + margin-top: -15px; + margin-bottom: 15px; +} + +#preprint-status { + .flex-container { + font-size: 16px; + display: flex; + align-items: center; + flex-direction: row; + align-content: space-between; + + strong { + display: inline-block; // required for the text-transform + } + strong::first-letter { + text-transform: uppercase; + } + .status-explanation { + padding-right: 15px; + } + .reviewer-feedback { + margin-left: auto; + padding-right: 15px; + + .dropdown-menu { + font-size:62.5%; + background-color: $color-bg-color-light; + color: $color-bg-color-grey; + border: 1px solid $color-border-light; + width: 445px; + left: auto; + right: 15px; + margin-top: 0px; + z-index: 98; // prevent dropdown from scrolling over navbar + + .status { + font-size: 1.4em; + } + + .moderator-comment { + font-size: 1.8em; + background-color: white; + padding: 30px 15px; + + p { + padding-bottom: 5px; + } + } + + .feedback-header { + font-size: 2.0em; + border-bottom: 1px solid $color-border-light; + padding: 5px 15px 8px 15px; + + a { + color: grey; + float: right; + } + } + + .feedback-footer { + border-top: 1px solid $color-border-light; + } + } + } + } +} + /* SUBMIT page */ .preprint-submit-header { diff --git a/app/templates/content/index.hbs b/app/templates/content/index.hbs index 8d604d885..e19077892 100644 --- a/app/templates/content/index.hbs +++ b/app/templates/content/index.hbs @@ -44,6 +44,9 @@ {{!END CONTAINER DIV}} {{!END HEADER ROW}} + {{#if isAdmin}} + {{preprint-status-banner submission=model}} + {{/if}}
{{!CONTAINER DIV}}
{{!CONTENT ROW}}
{{!LEFT COL DIV}} From 838331787d18e07b2fa6a331e097ba7e514631c9 Mon Sep 17 00:00:00 2001 From: Lauren Barker Date: Thu, 17 Aug 2017 11:27:32 -0400 Subject: [PATCH 02/19] Move logic out of template (#2) --- app/components/preprint-status-banner/component.js | 10 +++++++++- app/components/preprint-status-banner/template.hbs | 6 ++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/components/preprint-status-banner/component.js b/app/components/preprint-status-banner/component.js index f5ca1eaa9..ab0e69ba0 100644 --- a/app/components/preprint-status-banner/component.js +++ b/app/components/preprint-status-banner/component.js @@ -40,6 +40,7 @@ const CLASS_NAMES = { export default Ember.Component.extend({ + i18n: Ember.inject.service(), theme: Ember.inject.service(), // translations @@ -77,7 +78,14 @@ export default Ember.Component.extend({ reviewerComment:'', reviewerName: '', - bannerContent: Ember.computed('submission.provider.reviewsWorkflow', 'submission.reviewsState', function() { + bannerContent: Ember.computed('statusExplanation', 'workflow', function() { + let tName = this.get('theme.isProvider') ? this.get('theme.provider.name') : this.get('i18n').t('global.brand_name'); + let tWorkflow = this.get('i18n').t(this.get('workflow')); + let tStatusExplanation = this.get('i18n').t(this.get('statusExplanation')); + return `${this.get('i18n').t(this.get('baseMessage'), {name: tName, reviewsWorkflow: tWorkflow})} ${tStatusExplanation}.`; + }), + + statusExplanation: Ember.computed('submission.provider.reviewsWorkflow', 'submission.reviewsState', function() { if (this.get('submission.reviewsState') === PENDING) { return MESSAGE[this.get('submission.provider.reviewsWorkflow').toLowerCase()]; } diff --git a/app/components/preprint-status-banner/template.hbs b/app/components/preprint-status-banner/template.hbs index f30aa61ae..2a6ef2dc4 100644 --- a/app/components/preprint-status-banner/template.hbs +++ b/app/components/preprint-status-banner/template.hbs @@ -6,9 +6,7 @@
{{t status}}: - - {{t baseMessage name=(if theme.isProvider theme.provider.name (t "global.brand_name")) reviewsWorkflow=(t workflow)}} {{t bannerContent}}. - + {{bannerContent}}
{{#if (and reviewerComment (not submission.provider.reviewsCommentsPrivate))}}
{{t status}} -
{{t feedbackBaseMessage}} {{t bannerContent}}.
+
{{t feedbackBaseMessage}} {{t statusExplanation}}.

{{reviewerComment}}

From 0e005a2350556752c07fc9d7328278b6e209804a Mon Sep 17 00:00:00 2001 From: Lauren Barker Date: Fri, 25 Aug 2017 11:46:50 -0400 Subject: [PATCH 03/19] [MOD-52][Feature] Preprint submission updates (#3) * Remove submission block * Add logs on submit/resubmit * Expect null from reviewsWorkflow Make permission information language match * Assume API will lowercase values Fix typos * Chain save events Use firstObject Move promise to afterModel * Don't request the log unless comments are visible --- app/components/confirm-share-preprint.js | 2 + .../preprint-status-banner/component.js | 49 +++-- app/controllers/content/index.js | 13 ++ app/controllers/submit.js | 180 +++++++++++++++++- app/locales/en/translations.js | 61 ++++-- app/styles/app.scss | 22 +++ .../components/confirm-share-preprint.hbs | 4 +- app/templates/content/index.hbs | 6 +- app/templates/submit.hbs | 117 +++++++----- 9 files changed, 352 insertions(+), 102 deletions(-) diff --git a/app/components/confirm-share-preprint.js b/app/components/confirm-share-preprint.js index ca154d373..8776665cd 100644 --- a/app/components/confirm-share-preprint.js +++ b/app/components/confirm-share-preprint.js @@ -14,6 +14,8 @@ import Analytics from 'ember-osf/mixins/analytics'; * isOpen=showModalSharePreprint * shareButtonDisabled=shareButtonDisabled * savePreprint=(action 'savePreprint') + * title=title + * buttonLabel=buttonLabel *}} * ``` * @class confirm-share-preprint diff --git a/app/components/preprint-status-banner/component.js b/app/components/preprint-status-banner/component.js index ab0e69ba0..0db021b1e 100644 --- a/app/components/preprint-status-banner/component.js +++ b/app/components/preprint-status-banner/component.js @@ -27,8 +27,8 @@ const MESSAGE = { }; const WORKFLOW = { - [PRE_MODERATION]: 'components.preprint-status-banner.pre_moderation', - [POST_MODERATION]: 'components.preprint-status-banner.post_moderation' + [PRE_MODERATION]: 'global.pre_moderation', + [POST_MODERATION]: 'global.post_moderation' }; const CLASS_NAMES = { @@ -38,7 +38,6 @@ const CLASS_NAMES = { [REJECTED]: 'preprint-status-rejected' }; - export default Ember.Component.extend({ i18n: Ember.inject.service(), theme: Ember.inject.service(), @@ -53,43 +52,41 @@ export default Ember.Component.extend({ classNameBindings: ['getClassName'], getClassName: Ember.computed('submission.provider.reviewsWorkflow', 'submission.reviewsState', function() { - if (this.get('submission.reviewsState') === PENDING) { - return CLASS_NAMES[this.get('submission.provider.reviewsWorkflow').toLowerCase()] - } - return CLASS_NAMES[this.get('submission.reviewsState')]; - }), - - // TODO: move to 'content/index.hbs' once null value is updated - attributeBindings: ['hidden'], - hidden: Ember.computed('submission.provider.reviewsWorkflow', function() { - return this.get('submission.provider.reviewsWorkflow') === 'none'; + return this.get('submission.reviewsState') === PENDING ? + CLASS_NAMES[this.get('submission.provider.reviewsWorkflow')] : + CLASS_NAMES[this.get('submission.reviewsState')]; }), didReceiveAttrs() { - this.get('submission.reviewLogs').then(reviewLogs => { - let firstLog = reviewLogs.toArray()[0]; - this.set('reviewerComment', firstLog.get('comment')); - firstLog.get('creator').then(user => { - this.set('reviewerName', user.get('fullName')); - }) - }); + if (!this.get('submission.provider.reviewsCommentsPrivate')) { + this.get('submission.reviewLogs').then(reviewLogs => { + let firstLog = reviewLogs.toArray()[0]; + this.set('reviewerComment', firstLog.get('comment')); + firstLog.get('creator').then(user => { + this.set('reviewerName', user.get('fullName')); + }) + }); + } }, reviewerComment:'', reviewerName: '', bannerContent: Ember.computed('statusExplanation', 'workflow', function() { - let tName = this.get('theme.isProvider') ? this.get('theme.provider.name') : this.get('i18n').t('global.brand_name'); + let tName = this.get('theme.isProvider') ? + this.get('theme.provider.name') : + this.get('i18n').t('global.brand_name'); + let tWorkflow = this.get('i18n').t(this.get('workflow')); let tStatusExplanation = this.get('i18n').t(this.get('statusExplanation')); + return `${this.get('i18n').t(this.get('baseMessage'), {name: tName, reviewsWorkflow: tWorkflow})} ${tStatusExplanation}.`; }), statusExplanation: Ember.computed('submission.provider.reviewsWorkflow', 'submission.reviewsState', function() { - if (this.get('submission.reviewsState') === PENDING) { - return MESSAGE[this.get('submission.provider.reviewsWorkflow').toLowerCase()]; - } - return MESSAGE[this.get('submission.reviewsState')]; + return this.get('submission.reviewsState') === PENDING ? + MESSAGE[this.get('submission.provider.reviewsWorkflow')] : + MESSAGE[this.get('submission.reviewsState')]; }), status: Ember.computed('submission.reviewsState', function() { @@ -101,7 +98,7 @@ export default Ember.Component.extend({ }), workflow: Ember.computed('submission.provider.reviewsWorkflow', function () { - return WORKFLOW[this.get('submission.provider.reviewsWorkflow').toLowerCase()]; + return WORKFLOW[this.get('submission.provider.reviewsWorkflow')]; }), }); diff --git a/app/controllers/content/index.js b/app/controllers/content/index.js index 897cbce09..69ad40ceb 100644 --- a/app/controllers/content/index.js +++ b/app/controllers/content/index.js @@ -42,6 +42,12 @@ function queryStringify(queryParams) { return query.join('&'); } +const DATE_LABEL = { + created: 'content.date_label.created_on', + submitted: 'content.date_label.submitted_on' +} +const PRE_MODERATION = 'pre-moderation'; + /** * @module ember-preprints * @submodule controllers @@ -60,6 +66,13 @@ export default Ember.Controller.extend(Analytics, { queryParams: { chosenFile: 'file' }, + + logDateLabel: Ember.computed('model.provider.reviewsWorkflow', function() { + return this.get('model.provider.reviewsWorkflow') === PRE_MODERATION ? + DATE_LABEL['submitted'] : + DATE_LABEL['created']; + }), + isAdmin: Ember.computed('node', function() { // True if the current user has admin permissions for the node that contains the preprint return (this.get('node.currentUserPermissions') || []).includes(permissions.ADMIN); diff --git a/app/controllers/submit.js b/app/controllers/submit.js index 01d5be250..ef8296151 100644 --- a/app/controllers/submit.js +++ b/app/controllers/submit.js @@ -54,6 +54,63 @@ const BasicsValidations = buildValidations({ } }); +const PENDING = 'pending'; +const ACCEPTED = 'accepted'; +const REJECTED = 'rejected'; + +const PRE_MODERATION = 'pre-moderation'; +const POST_MODERATION = 'post-moderation'; + +const MODAL_TITLE = { + create: 'components.confirm-share-preprint.title.create', + submit: 'components.confirm-share-preprint.title.submit', + resubmit: 'components.confirm-share-preprint.title.resubmit' +}; + +const SUBMIT_MESSAGES = { + default: 'submit.body.submit.information.line1.default', + moderation: 'submit.body.submit.information.line1.moderation', + [PRE_MODERATION]: 'submit.body.submit.information.line3.pre', + [POST_MODERATION]: 'submit.body.submit.information.line3.post', +}; + +const PERMISSION_MESSAGES = { + create: 'submit.body.submit.information.line2.create', + submit: 'submit.body.submit.information.line2.submit' +} + +const EDIT_MESSAGES = { + line1: { + [PRE_MODERATION]: 'submit.body.edit.information.line1.pre', + [POST_MODERATION]: 'submit.body.edit.information.line1.post_rejected' + }, + line2: { + [PENDING]: { + [PRE_MODERATION]: 'submit.body.edit.information.line2.pre_pending', + }, + [REJECTED]: { + [PRE_MODERATION]: 'submit.body.edit.information.line2.pre_rejected', + [POST_MODERATION]: 'submit.body.edit.information.line2.post_rejected' + } + } +}; + +const WORKFLOW = { + [PRE_MODERATION]: 'global.pre_moderation', + [POST_MODERATION]: 'global.post_moderation' +}; + +const ACTION = { + create: { + heading: 'submit.create_heading', + button: 'submit.body.submit.create_button' + }, + submit: { + heading: 'submit.submit_heading', + button: 'submit.body.submit.submit_button' + } +}; + function subjectIdMap(subjectArray) { // Maps array of arrays of disciplines into array of arrays of discipline ids. return subjectArray.map(subjectBlock => subjectBlock.map(subject => subject.id)); @@ -77,7 +134,7 @@ export default Ember.Controller.extend(Analytics, BasicsValidations, NodeActions filePickerState: State.START, // Selected upload state (initial decision on form) - new or existing project? (is poorly named) _existingState: existingState, // File states - new file or existing file existingState: existingState.CHOOSE, // Selected file state - new or existing file (poorly named) - _names: ['upload', 'discipline', 'basics', 'authors', 'submit'].map(str => str.capitalize()), // Form section headers + _names: ['upload', 'discipline', 'basics', 'authors'].map(str => str.capitalize()), // Form section headers // Data for project picker; tracked internally on load user: null, @@ -122,7 +179,6 @@ export default Ember.Controller.extend(Analytics, BasicsValidations, NodeActions clearFields() { // Restores submit form defaults. Called when user submits preprint, then hits back button, for example. this.get('panelActions').open('Upload'); - this.get('panelActions').close('Submit'); this.setProperties(Ember.merge( this.get('_names').reduce((acc, name) => Ember.merge(acc, {[`${name.toLowerCase()}SaveState`]: false}), {}), { @@ -359,6 +415,70 @@ export default Ember.Controller.extend(Analytics, BasicsValidations, NodeActions return this.get('isAdmin') && !(this.get('node.registration')); }), + /////////////////////////////////////////////////// + // Language about submission and moderation. + //////////////////////////////////////////////////// + + moderationType: Ember.computed.alias('theme.provider.reviewsWorkflow'), + workflow: Ember.computed('moderationType', function () { + return WORKFLOW[this.get('moderationType')]; + }), + providerName: Ember.computed('theme.isProvider', function() { + return this.get('theme.isProvider') ? + this.get('theme.provider.name') : + this.get('i18n').t('global.brand_name'); + }), + modalTitle: Ember.computed('moderationType', function() { + if (this.get('editMode')) { + return MODAL_TITLE['resubmit']; + } + return this.get('moderationType') === PRE_MODERATION ? + MODAL_TITLE['submit'] : + MODAL_TITLE['create']; + }), + + // submission + heading: Ember.computed('moderationType', function() { + return this.get('moderationType') === PRE_MODERATION ? + ACTION['submit']['heading'] : + ACTION['create']['heading']; + }), + buttonLabel: Ember.computed('moderationType', function() { + return this.get('moderationType') === PRE_MODERATION ? + ACTION['submit']['button'] : + ACTION['create']['button']; + }), + generalInformation: Ember.computed('moderationType', function() { + return this.get('moderationType') ? + SUBMIT_MESSAGES['moderation'] : + SUBMIT_MESSAGES['default']; + }), + permissionInformation: Ember.computed('moderationType', function() { + return this.get('moderationType') === PRE_MODERATION ? + PERMISSION_MESSAGES['submit'] : + PERMISSION_MESSAGES['create']; + }), + moderationInformation: Ember.computed('moderationType', function() { + return SUBMIT_MESSAGES[this.get('moderationType')]; + }), + + // edit + showInformation: Ember.computed('moderationType', 'model.reviewsState', function() { + let state = this.get('model.reviewsState'); + let modType = this.get('moderationType'); + return !(state === ACCEPTED || (modType === POST_MODERATION && state === PENDING)); + }), + editInformation1: Ember.computed('moderationType', function() { + return EDIT_MESSAGES['line1'][this.get('moderationType')]; + }), + editInformation2: Ember.computed('moderationType', 'model.reviewsState', function() { + return EDIT_MESSAGES['line2'][this.get('model.reviewsState')][this.get('moderationType')]; + }), + canResubmit: Ember.computed('moderationType', 'model.reviewsState', function() { + let state = this.get('model.reviewsState'); + return this.get('moderationType') === PRE_MODERATION && (state === PENDING || state === REJECTED); + }), + actions: { // This gets called by the save method of the license-widget, which in autosave mode // gets called every time a change is observed in the widget. @@ -899,15 +1019,57 @@ export default Ember.Controller.extend(Analytics, BasicsValidations, NodeActions const node = this.get('node'); this.set('savingPreprint', true); this.toggleProperty('shareButtonDisabled'); - model.set('isPublished', true); + + let log = null; + if (this.get('moderationType')) { + log = this.get('store').createRecord('reviewLog', { + action: 'submit', + reviewable: this.get('model') + }); + } else { + model.set('isPublished', true); + } node.set('public', true); - return model.save() - .then(() => node.save()) + let save_changes = null; + if (log) { + save_changes = model.save().then(() => node.save()).then(() => log.save()); + } else { + save_changes = model.save().then(() => node.save()); + } + + return save_changes + .then(() => { + this.transitionToRoute( + `${this.get('theme.isSubRoute') ? 'provider.' : ''}content`, + model.reload() + ); + }) + .catch(() => { + this.toggleProperty('shareButtonDisabled'); + return this.get('toast') + .error(this.get('i18n') + .t(`submit.error_${this.get('editMode') ? 'completing' : 'saving'}_preprint`) + ); + }); + }, + cancel() { + this.transitionToRoute('index'); + }, + resubmit() { + this.set('savingPreprint', true); + this.toggleProperty('shareButtonDisabled'); + + let log = this.get('store').createRecord('reviewLog', { + action: 'submit', + reviewable: this.get('model') + }); + + return log.save() .then(() => { this.transitionToRoute( `${this.get('theme.isSubRoute') ? 'provider.' : ''}content`, - model + this.get('model') ); }) .catch(() => { @@ -918,5 +1080,11 @@ export default Ember.Controller.extend(Analytics, BasicsValidations, NodeActions ); }); }, + returnToSubmission() { + this.transitionToRoute( + `${this.get('theme.isSubRoute') ? 'provider.' : ''}content`, + this.get('model') + ); + }, } }); diff --git a/app/locales/en/translations.js b/app/locales/en/translations.js index 9700aa4e2..850e0deb8 100644 --- a/app/locales/en/translations.js +++ b/app/locales/en/translations.js @@ -33,6 +33,8 @@ export default { copy_inside_project: `The {{preprintWords.preprint}} will be organized in a new component`, open_science_framework: `Open Science Framework`, license: 'License', + pre_moderation: `pre-moderation`, + post_moderation: `post-moderation`, }, application: { separator: ` | ` @@ -41,6 +43,10 @@ export default { header: { last_edited: `Last edited` }, + date_label: { + created_on: `Created on`, + submitted_on: `Submitted on` + }, share: { download: `Download`, downloads: `Downloads`, @@ -149,11 +155,12 @@ export default { message: `User has deleted this content.` }, submit: { - add_heading: `Add {{preprintWords.Preprint}}`, + create_heading: `Create {{preprintWords.Preprint}}`, + submit_heading: `Submit {{preprintWords.Preprint}}`, edit_heading: `Edit {{preprintWords.Preprint}}`, body: { - p_add: `Follow these five easy steps to add your {{preprintWords.preprint}} to the {{name}} {{preprintWords.preprint}} repository.`, - p_edit: `Edit your {{preprintWords.preprint}} sections below.`, + p_add: `For each section below, "Save and continue" will directly update the OSF project.`, + p_edit: `For each section below, "Save and continue" will directly update the {{preprintWords.preprint}} and the OSF project.`, upload: `Upload new {{preprintWords.preprint}}`, connect: `Connect {{preprintWords.preprint}} to existing OSF project`, file: `{{preprintWords.Preprint}} file`, @@ -180,19 +187,43 @@ export default { paragraph: `Add {{preprintWords.preprint}} authors and order them appropriately. All changes to authors are saved immediately. Search looks for authors that have OSF accounts already. Unregistered users can be added and invited to join the {{preprintWords.preprint}}.` }, submit: { - paragraph: { - line1: `When you share this {{preprintWords.preprint}}, the DOI will be created and the {{preprintWords.preprint}} will become publicly accessible via {{name}} {{preprintWords.Preprints}}. You will be unable to delete the {{preprintWords.preprint}} file, but you can update or modify it. This also creates an OSF project in case you would like to attach other content to your {{preprintWords.preprint}} such as supplementary materials, appendices, data, or protocols. If posting this {{preprintWords.preprint}} is your first exposure to the OSF, you will receive an email introducing OSF to you.`, - line2: `By clicking Share, you confirm that all Contributors agree with sharing this {{preprintWords.preprint}}, and that you have the right to share it.` + information: { + line1: { + default: `When you create this {{preprintWords.preprint}}, it will be assigned a DOI and become publicly accessible via {{name}}. The {{preprintWords.preprint}} file cannot be deleted, but it can be updated or modified. The related OSF project can be used to manage supplementary materials, appendices, data, or protocols for your {{preprintWords.preprint}}.`, + moderation: `When you submit this {{preprintWords.preprint}}, it will be assigned a DOI. The {{preprintWords.preprint}} file cannot be deleted, but it can be updated or modified. The related OSF project can be used to manage supplementary materials, appendices, data, or protocols for your {{preprintWords.preprint}}.`, + }, + line2: { + create: `By creating this {{preprintWords.preprint}}, you confirm that all Contributors agree with sharing it, and that you have the right to share this {{preprintWords.preprint}}.`, + submit: `By submitting this {{preprintWords.preprint}}, you confirm that all Contributors agree with sharing it, and that you have the right to share this {{preprintWords.preprint}}.` + }, + line3: { + pre: `{{name}} uses {{reviewsWorkflow}}, therefore your {{preprintWords.preprint}} will not become publicly accessible until a moderator accepts the submission.`, + post: `{{name}} uses {{reviewsWorkflow}}, therefore your {{preprintWords.preprint}} will become publicly accessible after creation and will only become private if rejected by a moderator.`, + } }, invalid: { - description: `The following section(s) must be completed before sharing this {{preprintWords.preprint}}.`, + description: `The following section(s) must be completed before submitting this {{preprintWords.preprint}}.`, discipline: `Discipline`, basics: `Basics`, upload: `Upload` - } + }, + submit_button: `Submit {{preprintWords.preprint}}`, + create_button: `Create {{preprintWords.preprint}}`, }, - update: { - paragraph: `Edits to this {{preprintWords.preprint}} will update both the {{preprintWords.preprint}} and the OSF project and will become publicly accessible via {{name}} {{preprintWords.Preprints}}.` + edit: { + information: { + line1: { + pre: `{{name}} uses {{reviewsWorkflow}}, therefore your {{preprintWords.preprint}} will not become publicly accessible until a moderator accepts the submission.`, + post_rejected: `{{name}} uses {{reviewsWorkflow}}. Your {{preprintWords.preprint}} was rejected by a moderator so it will remain private.`, + }, + line2: { + pre_pending: `Resubmitting may affect your position in the moderation queue.`, + pre_rejected: `Once you have addressed any moderator concerns, you may resubmit for review.`, + post_rejected: `If you have addressed all moderator concerns or believe there has been a mistake, please email {{email}} to discuss resubmission.` + } + }, + resubmit_button: `Resubmit`, + return_button: `Return to {{preprintWords.preprint}}` }, save_continue: `Save and continue`, }, @@ -221,8 +252,12 @@ export default { body: `Are you sure you want to restart this {{preprintWords.preprint}}? Your uploaded file and supplemental information will be deleted.` }, 'confirm-share-preprint': { - title: `Share {{preprintWords.Preprint}}`, - body: `Once this {{preprintWords.preprint}} is made public, you should assume that it will always be public. Even if you delete it, search engines or others may access the files before you do so.` + body: `Once this {{preprintWords.preprint}} is made public, you should assume that it will always be public. Even if you delete it, search engines or others may access the files before you do so.`, + title: { + submit: `Submit {{preprintWords.Preprint}}`, + create: `Create {{preprintWords.Preprint}}`, + resubmit: `Resubmit {{preprintWords.Preprint}}` + } }, 'convert-or-copy': { organize_language_project: `You can organize your {{preprintWords.preprint}} by storing the file in this project or in its own new component. If you select ‘Make a new component’, @@ -360,8 +395,6 @@ export default { moderator: `Moderator`, base: `This {{preprintWords.preprint}} is`, }, - pre_moderation: `pre-moderation`, - post_moderation: `post-moderation`, }, 'project-chooser': { file_upload_create: `Upload a file and create an OSF project`, diff --git a/app/styles/app.scss b/app/styles/app.scss index 509fc7511..a69acacc8 100644 --- a/app/styles/app.scss +++ b/app/styles/app.scss @@ -487,6 +487,28 @@ $color-border-light: #DDDDDD; background-color: #f8f8f8; border-bottom: 1px solid #dddddd; padding-bottom: 50px; + + .instructions { + font-size: 18px; + } + + .submit-section { + font-size: 18px; + padding-top: 20px; + + #validation-errors { + margin-bottom: 10px; + } + + .information { + margin-bottom: 15px; + } + + button { + font-size: 18px; + margin-left: 15px; + } + } } .preprint-form-block { diff --git a/app/templates/components/confirm-share-preprint.hbs b/app/templates/components/confirm-share-preprint.hbs index d85e3dbfd..9df76e3b6 100644 --- a/app/templates/components/confirm-share-preprint.hbs +++ b/app/templates/components/confirm-share-preprint.hbs @@ -1,6 +1,6 @@ {{#bs-modal open=isOpen body=false header=false footer=false}} {{#bs-modal-header}} - + {{/bs-modal-header}} {{#bs-modal-body}} {{t "components.confirm-share-preprint.body"}} @@ -9,7 +9,7 @@ {{#bs-modal-footer}}
- +
{{/bs-modal-footer}} {{/bs-modal}} diff --git a/app/templates/content/index.hbs b/app/templates/content/index.hbs index e19077892..5d0e55cf7 100644 --- a/app/templates/content/index.hbs +++ b/app/templates/content/index.hbs @@ -25,7 +25,9 @@ {{/if}} -
{{t "global.added_on"}}: {{moment-format model.datePublished "MMMM DD, YYYY"}} | {{t "content.header.last_edited"}}: {{moment-format model.dateModified "MMMM DD, YYYY"}}
+
+ {{t logDateLabel}}: {{moment-format model.dateLastTransitioned "MMMM DD, YYYY"}} | {{t "content.header.last_edited"}}: {{moment-format model.dateModified "MMMM DD, YYYY"}} +
{{!END CONTAINER DIV}}
{{!END HEADER ROW}} - {{#if isAdmin}} + {{#if (and isAdmin model.provider.reviewsWorkflow)}} {{preprint-status-banner submission=model}} {{/if}}
{{!CONTAINER DIV}} diff --git a/app/templates/submit.hbs b/app/templates/submit.hbs index 61fa45ee5..aac7cac8a 100644 --- a/app/templates/submit.hbs +++ b/app/templates/submit.hbs @@ -4,17 +4,18 @@
-

{{t (if editMode "submit.edit_heading" "submit.add_heading")}}

+

{{t (if editMode "submit.edit_heading" heading)}}

-
-

{{if editMode (t "submit.body.p_edit") (t "submit.body.p_add" name=(if theme.isProvider theme.provider.name (t "global.brand_name")))}}

+

+ {{if editMode (t "submit.body.p_edit") (t "submit.body.p_add" name=(if theme.isProvider theme.provider.name (t "global.brand_name")))}} +

{{#cp-panels accordion=true}} {{#with _names.[0] as |name|}} {{#preprint-form-section id='preprint-form-upload' editMode=editMode class="preprint-form-block" open=true name=name allowOpen=true errorAction=(action 'error') as |hasOpened|}} @@ -217,59 +218,71 @@ parentNode=parentNode editMode=editMode }} -
-
-
- -
-
-
{{/preprint-form-body}} {{/preprint-form-section}} {{/with}} - {{#with _names.[4] as |name|}} - {{#preprint-form-section id='preprint-form-submit' editMode=editMode class="preprint-form-block" animate=true name=name allowOpen=uploadValid errorAction=(action 'error') as |hasOpened|}} - {{preprint-form-header editMode=editMode name=(if editMode 'Update' name) showValidationIndicator=false}} - {{#if editMode}} - {{#cp-panel-body}} -
-
-

- {{t "submit.body.update.paragraph" name=(if theme.isProvider theme.provider.name (t "global.brand_name"))}} -

- -
-
- {{/cp-panel-body}} - {{else}} - {{#cp-panel-body}} -
-

- {{t "submit.body.submit.paragraph.line1" name=(if theme.isProvider theme.provider.name (t "global.brand_name"))}} -

-

- {{t "submit.body.submit.paragraph.line2" name=(if theme.isProvider theme.provider.name (t "global.brand_name"))}} -

-
- {{#unless allSectionsValid}} -

{{t "submit.body.submit.invalid.description"}}

-

{{unless (and savedTitle savedFile) (t "submit.body.submit.invalid.upload")}}

-

{{unless savedSubjects (t "submit.body.submit.invalid.discipline")}}

-

{{unless savedAbstract (t "submit.body.submit.invalid.basics")}}

-

{{unless authorsValid (t "global.authors")}}

- {{/unless}} -

- -
- {{confirm-share-preprint isOpen=showModalSharePreprint shareButtonDisabled=shareButtonDisabled savePreprint=(action 'savePreprint')}} -
-
-
- {{/cp-panel-body}} - {{/if}} - {{/preprint-form-section}} - {{/with}} {{/cp-panels}} +
+ {{#if editMode}} +
+ {{#if showInformation}} +

{{t editInformation1 name=providerName reviewsWorkflow=(t workflow)}}

+

{{t editInformation2 name=providerName reviewsWorkflow=(t workflow)}}

+ {{/if}} + {{#if canResubmit}} + +
+ {{confirm-share-preprint + isOpen=showModalSharePreprint + shareButtonDisabled=shareButtonDisabled + savePreprint=(action 'resubmit') + buttonLabel="submit.body.edit.resubmit_button" + title=modalTitle + }} +
+ {{/if}} + +
+ {{else}} +

{{t generalInformation name=providerName}}

+

{{t permissionInformation name=providerName}}

+ {{#if moderationType}} +

+ {{t moderationInformation name=providerName reviewsWorkflow=(t workflow)}} +

+ {{/if}} +
+ {{#unless allSectionsValid}} + +

{{t "submit.body.submit.invalid.description"}}

+

{{unless (and savedTitle savedFile) (t "submit.body.submit.invalid.upload")}}

+

{{unless savedSubjects (t "submit.body.submit.invalid.discipline")}}

+

{{unless savedAbstract (t "submit.body.submit.invalid.basics")}}

+

{{unless authorsValid (t "global.authors")}}

+
+ {{/unless}} + + +
+ {{confirm-share-preprint + isOpen=showModalSharePreprint + shareButtonDisabled=shareButtonDisabled + savePreprint=(action 'savePreprint') + buttonLabel=buttonLabel + title=modalTitle + }} +
+
+ {{/if}} +
From 24a13cd9d0a913007e5e83e0ebfdf7fee9073a3c Mon Sep 17 00:00:00 2001 From: Abram Booth Date: Thu, 14 Sep 2017 12:11:03 -0400 Subject: [PATCH 04/19] Rename review-log to action --- .../preprint-status-banner/component.js | 8 ++++---- app/controllers/submit.js | 20 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/components/preprint-status-banner/component.js b/app/components/preprint-status-banner/component.js index 0db021b1e..878f887a3 100644 --- a/app/components/preprint-status-banner/component.js +++ b/app/components/preprint-status-banner/component.js @@ -59,10 +59,10 @@ export default Ember.Component.extend({ didReceiveAttrs() { if (!this.get('submission.provider.reviewsCommentsPrivate')) { - this.get('submission.reviewLogs').then(reviewLogs => { - let firstLog = reviewLogs.toArray()[0]; - this.set('reviewerComment', firstLog.get('comment')); - firstLog.get('creator').then(user => { + this.get('submission.actions').then(actions => { + let firstAction = actions.toArray()[0]; + this.set('reviewerComment', firstAction.get('comment')); + firstAction.get('creator').then(user => { this.set('reviewerName', user.get('fullName')); }) }); diff --git a/app/controllers/submit.js b/app/controllers/submit.js index ef8296151..0c3888c1a 100644 --- a/app/controllers/submit.js +++ b/app/controllers/submit.js @@ -1020,11 +1020,11 @@ export default Ember.Controller.extend(Analytics, BasicsValidations, NodeActions this.set('savingPreprint', true); this.toggleProperty('shareButtonDisabled'); - let log = null; + let submitAction = null; if (this.get('moderationType')) { - log = this.get('store').createRecord('reviewLog', { - action: 'submit', - reviewable: this.get('model') + submitAction = this.get('store').createRecord('action', { + actionTrigger: 'submit', + target: this.get('model') }); } else { model.set('isPublished', true); @@ -1032,8 +1032,8 @@ export default Ember.Controller.extend(Analytics, BasicsValidations, NodeActions node.set('public', true); let save_changes = null; - if (log) { - save_changes = model.save().then(() => node.save()).then(() => log.save()); + if (submitAction) { + save_changes = model.save().then(() => node.save()).then(() => submitAction.save()); } else { save_changes = model.save().then(() => node.save()); } @@ -1060,12 +1060,12 @@ export default Ember.Controller.extend(Analytics, BasicsValidations, NodeActions this.set('savingPreprint', true); this.toggleProperty('shareButtonDisabled'); - let log = this.get('store').createRecord('reviewLog', { - action: 'submit', - reviewable: this.get('model') + let submitAction = this.get('store').createRecord('action', { + trigger: 'submit', + target: this.get('model') }); - return log.save() + return submitAction.save() .then(() => { this.transitionToRoute( `${this.get('theme.isSubRoute') ? 'provider.' : ''}content`, From 6a41e97dd940a4dd50b308194f40467afae958db Mon Sep 17 00:00:00 2001 From: Abram Booth Date: Fri, 15 Sep 2017 10:51:06 -0400 Subject: [PATCH 05/19] Fix failing test --- tests/unit/controllers/submit-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/controllers/submit-test.js b/tests/unit/controllers/submit-test.js index d5246c38e..80cd42411 100644 --- a/tests/unit/controllers/submit-test.js +++ b/tests/unit/controllers/submit-test.js @@ -81,7 +81,7 @@ test('Initial properties', function (assert) { '_existingState.EXISTINGFILE': 'existing', '_existingState.NEWFILE': 'new', 'existingState': 'choose', - '_names.length': 5, + '_names.length': 4, 'user': null, 'userNodes.length': 0, 'userNodesLoaded': false, From 19f81faa798693ea3f2faca8c78f24f2abb901fe Mon Sep 17 00:00:00 2001 From: Abram Booth Date: Tue, 19 Sep 2017 10:54:59 -0400 Subject: [PATCH 06/19] Update ember-osf dependency --- package.json | 2 +- yarn.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 364ee258c..6314f0f99 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "author": "", "license": "Apache-2.0", "devDependencies": { - "@centerforopenscience/ember-osf": "https://github.com/CenterForOpenScience/ember-osf.git#develop#2017-09-13T14:33:43Z", + "@centerforopenscience/ember-osf": "https://github.com/aaxelb/ember-osf.git#feature/reviews-minimal", "@centerforopenscience/osf-style": "1.5.0", "autoprefixer": "6.3.7", "broccoli-asset-rev": "2.4.2", diff --git a/yarn.lock b/yarn.lock index 6756d4b21..37dde6758 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@centerforopenscience/ember-osf@https://github.com/CenterForOpenScience/ember-osf.git#develop#2017-09-13T14:33:43Z": +"@centerforopenscience/ember-osf@https://github.com/aaxelb/ember-osf.git#feature/reviews-minimal": version "0.10.2" - resolved "https://github.com/CenterForOpenScience/ember-osf.git#d1be029cdbc58fc0a5a05f1b21b8b542ac50559a" + resolved "https://github.com/aaxelb/ember-osf.git#6ad3b8944160045b45ca3081ea77249460c18eb5" dependencies: broccoli-funnel "1.2.0" broccoli-merge-trees "2.0.0" From a6e139ef48c21e1238619bfa3f445c1838fbcbf8 Mon Sep 17 00:00:00 2001 From: Abram Booth Date: Tue, 19 Sep 2017 16:15:05 -0400 Subject: [PATCH 07/19] Fix resubmission (#2) --- app/controllers/submit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/submit.js b/app/controllers/submit.js index 0c3888c1a..95fe9f81a 100644 --- a/app/controllers/submit.js +++ b/app/controllers/submit.js @@ -1061,8 +1061,8 @@ export default Ember.Controller.extend(Analytics, BasicsValidations, NodeActions this.toggleProperty('shareButtonDisabled'); let submitAction = this.get('store').createRecord('action', { - trigger: 'submit', - target: this.get('model') + actionTrigger: 'submit', + target: this.get('model') }); return submitAction.save() From e787625c595cc779961ce8bcb3d71a295f1eeaf5 Mon Sep 17 00:00:00 2001 From: Lauren Barker Date: Wed, 20 Sep 2017 14:21:01 -0400 Subject: [PATCH 08/19] Fix missing support email (#3) --- app/templates/submit.hbs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/templates/submit.hbs b/app/templates/submit.hbs index aac7cac8a..1bf4f089b 100644 --- a/app/templates/submit.hbs +++ b/app/templates/submit.hbs @@ -227,7 +227,12 @@
{{#if showInformation}}

{{t editInformation1 name=providerName reviewsWorkflow=(t workflow)}}

-

{{t editInformation2 name=providerName reviewsWorkflow=(t workflow)}}

+

+ {{t editInformation2 + name=providerName + reviewsWorkflow=(t workflow) + email=theme.provider.emailSupport}} +

{{/if}} {{#if canResubmit}}