Skip to content
This repository was archived by the owner on Apr 14, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ede1b7a
[MOD-57][Feature] Add preprint status banner (#1)
laurenbarker Aug 17, 2017
8383317
Move logic out of template (#2)
laurenbarker Aug 17, 2017
0e005a2
[MOD-52][Feature] Preprint submission updates (#3)
laurenbarker Aug 25, 2017
24a13cd
Rename review-log to action
aaxelb Sep 14, 2017
6a41e97
Fix failing test
aaxelb Sep 15, 2017
19f81fa
Update ember-osf dependency
aaxelb Sep 19, 2017
a6e139e
Fix resubmission (#2)
aaxelb Sep 19, 2017
e787625
Fix missing support email (#3)
laurenbarker Sep 20, 2017
4035e7d
Add reviews link to branded navbars (#4)
laurenbarker Sep 20, 2017
df6b987
Fix invalid date
laurenbarker Sep 22, 2017
88816ce
Merge pull request #6 from laurenbarker/feature/fix-date
laurenbarker Sep 22, 2017
00f548a
Fix indentation (#5)
laurenbarker Sep 25, 2017
3f2914b
Fix tests (#7)
aaxelb Sep 25, 2017
3b4e56c
Update ember-osf dependency
aaxelb Sep 25, 2017
0842fef
[MOD-88][Fix] Don't show errors until user clicks submit (#8)
aaxelb Sep 26, 2017
8438be6
Update ember-osf version
aaxelb Sep 27, 2017
9cb9b51
Merge remote-tracking branch 'upstream/develop' into feature/reviews
aaxelb Sep 27, 2017
cb6db76
Update ember-osf commit
laurenbarker Oct 2, 2017
4164b12
Merge pull request #9 from laurenbarker/feature/update-lock-file
laurenbarker Oct 2, 2017
1b9979b
Upgrade ember-osf dependency
aaxelb Oct 5, 2017
acd99b6
Update ember-osf dependency
aaxelb Oct 5, 2017
ed3daee
yarn
aaxelb Oct 6, 2017
42cbb4d
Handle preprints with no actions
aaxelb Sep 28, 2017
d76479e
Merge pull request #462 from aaxelb/fix/MOD-84
aaxelb Oct 10, 2017
f4862c1
[MOD-138][Improvement] Show status banner to contributors (#463)
laurenbarker Oct 11, 2017
93ec132
Update wording on status banner to make more clear (#464)
laurenbarker Oct 11, 2017
a951c3d
Merge branch 'develop' into feature/reviews
aaxelb Oct 12, 2017
7b91ce5
Hide status banner on private and abandoned preprints (#466)
aaxelb Oct 16, 2017
46612d7
Update ember-osf dependency
aaxelb Oct 17, 2017
cf193ab
Update CHANGELOG
aaxelb Oct 17, 2017
470effc
Downgrade ember-osf to get the old file browser
aaxelb Oct 17, 2017
144d403
Refresh model after resubmission (#472)
laurenbarker Oct 20, 2017
d3c5df7
Update ember-osf dependency
aaxelb Oct 23, 2017
4f4c807
Merge branch 'develop' into feature/reviews
aaxelb Oct 23, 2017
afdd0a2
Use old-file-browser
hmoco Oct 18, 2017
f3f9e4e
Update ember-osf dependency
aaxelb Oct 23, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- `preprint-status-banner` component, used on the preprint detail page to show contributors the state of their preprint in a reviews workflow
- "My Reviewing" link in navbar, if the user is a moderator for a preprint provider

### Changed
- Update submit/edit page to support reviews workflows
- Update language depending on the provider's workflow
- Create `action` on preprint submission, instead of setting `is_published: true`

### Removed

## [0.114.3] - 2017-10-20
### Added
Expand Down
2 changes: 2 additions & 0 deletions app/components/confirm-share-preprint.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions app/components/preprint-navbar-branded.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import config from 'ember-get-config';
export default Ember.Component.extend(OSFAgnosticAuthControllerMixin, Analytics, {
session: Ember.inject.service(),
theme: Ember.inject.service(),
currentUser: Ember.inject.service(),

tagName: 'nav',
classNames: ['navbar', 'branded-navbar', 'preprint-navbar'],
host: config.OSF.url,
Expand Down
108 changes: 108 additions & 0 deletions app/components/preprint-status-banner/component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
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]: 'global.pre_moderation',
[POST_MODERATION]: 'global.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({
i18n: Ember.inject.service(),
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() {
return this.get('submission.reviewsState') === PENDING ?
CLASS_NAMES[this.get('submission.provider.reviewsWorkflow')] :
CLASS_NAMES[this.get('submission.reviewsState')];
}),

didReceiveAttrs() {
if (this.get('submission.provider.reviewsCommentsPrivate')) {
this.set('latestAction', null);
} else {
this.get('submission.actions').then(actions => {
if (actions.length) {
this.set('latestAction', actions.get('firstObject'));
} else {
this.set('latestAction', null);
}
});
}
},

latestAction: null,
reviewerComment: Ember.computed.alias('latestAction.comment'),
reviewerName: Ember.computed.alias('latestAction.creator.fullName'),

bannerContent: Ember.computed('statusExplanation', 'workflow', 'theme.{isProvider,provider.name}', function() {
const i18n = this.get('i18n');
const tName = this.get('theme.isProvider') ?
this.get('theme.provider.name') :
i18n.t('global.brand_name');

const tWorkflow = i18n.t(this.get('workflow'));
const tStatusExplanation = i18n.t(this.get('statusExplanation'));

return `${i18n.t(this.get('baseMessage'), {name: tName, reviewsWorkflow: tWorkflow})} ${tStatusExplanation}.`;
}),

statusExplanation: Ember.computed('submission.provider.reviewsWorkflow', 'submission.reviewsState', function() {
return this.get('submission.reviewsState') === PENDING ?
MESSAGE[this.get('submission.provider.reviewsWorkflow')] :
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')];
}),

});
40 changes: 40 additions & 0 deletions app/components/preprint-status-banner/template.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<div id="preprint-status" class="container p-md">
<div class="flex-container">
{{#if submission.provider.isPending}}
Loading...
{{else}}
<div class="status-explanation">
<i class="fa {{icon}} status-icon" aria-hidden="true"></i>
<strong>{{t status}}:</strong>
<span>{{bannerContent}}</span>
</div>
{{#if (and reviewerComment (not submission.provider.reviewsCommentsPrivate))}}
<div class="dropdown reviewer-feedback">
<button class="btn btn-default dropdown-toggle feedback-btn" type="button" data-toggle="dropdown">
{{t labelModeratorFeedback}} <i class="fa fa-caret-down" aria-hidden="true"></i>
</button>
<div class="dropdown-menu">
<div class="feedback-header">
<span id="moderator-feedback">{{t labelModeratorFeedback}}</span>
<a role="button" aria-label="Close" href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-times"></i>
</a>
</div>
<div class="status p-md">
<strong>{{t status}}</strong>
<div>{{t feedbackBaseMessage}} {{t statusExplanation}}.</div>
</div>
<div class="moderator-comment" aria-labelledby="moderator-feedback">
<p>{{reviewerComment}}</p>
{{#if (not submission.provider.reviewsCommentsAnonymous)}}
<div>{{reviewerName}}</div>
{{/if}}
{{if theme.isProvider theme.provider.name (t "global.brand_name")}} {{t moderator}}
</div>
<div class="feedback-footer p-md"></div>
</div>
</div>
{{/if}}
{{/if}}
</div>
</div>
62 changes: 43 additions & 19 deletions app/controllers/content/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -60,10 +66,44 @@ export default Ember.Controller.extend(Analytics, {
queryParams: {
chosenFile: 'file'
},

dateLabel: Ember.computed('model.provider.reviewsWorkflow', function() {
return this.get('model.provider.reviewsWorkflow') === PRE_MODERATION ?
DATE_LABEL['submitted'] :
DATE_LABEL['created'];
}),
relevantDate: Ember.computed('model.provider.reviewsWorkflow', function() {
return this.get('model.provider.reviewsWorkflow') ?
this.get('model.dateLastTransitioned') :
this.get('model.dateCreated');
}),

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);
}),

userIsContrib: Ember.computed('authors.[]', 'isAdmin', 'currentUser.currentUserId', function() {
if (this.get('isAdmin')) {
return true;
} else if (this.get('authors').length) {
const authorIds = this.get('authors').map((author) => {
return author.get('userId');
});
return this.get('currentUser.currentUserId') ? authorIds.includes(this.get('currentUser.currentUserId')) : false;
}
return false;
}),

showStatusBanner: Ember.computed('model.{provider.reviewsWorkflow,reviewsState}', 'userIsContrib', 'node.public', function() {
return (
this.get('model.provider.reviewsWorkflow')
&& this.get('node.public')
&& this.get('userIsContrib')
&& this.get('model.reviewsState') !== 'initial'
);
}),

twitterHref: Ember.computed('node', function() {
const queryParams = {
url: window.location.href,
Expand Down Expand Up @@ -214,8 +254,6 @@ export default Ember.Controller.extend(Analytics, {
// to track non contributor preprint downloads specifically.
dualTrackNonContributors(category, label, url, primary) {
this.send('click', category, label, url); // Sends event to both Google Analytics and Keen.
const authors = this.get('authors');
let userIsContrib = false;

const eventData = {
download_info: {
Expand Down Expand Up @@ -243,23 +281,9 @@ export default Ember.Controller.extend(Analytics, {
node: this.get('node'),
};

this.get('currentUser').load()
.then(user => {
if (user) {
const userId = user.id;
authors.forEach((author) => {
if (author.get('userId') === userId) {
userIsContrib = true;
}
});
}
if (!userIsContrib) {
Ember.get(this, 'metrics').invoke('trackSpecificCollection', 'Keen', keenPayload); // Sends event to Keen if logged-in user is not a contributor
}
})
.catch(() => {
Ember.get(this, 'metrics').invoke('trackSpecificCollection', 'Keen', keenPayload); // Sends event to Keen for non-authenticated user
});
if (!this.get('userIsContrib')) {
Ember.get(this, 'metrics').invoke('trackSpecificCollection', 'Keen', keenPayload); // Sends event to Keen if logged-in user is not a contributor or non-authenticated user
}
}
},
});
Loading