Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENG-6813] FE PR for Preprints DOI Versioning #2447

Merged
merged 24 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
500e4bd
[ENG-6447] Update preprint model for versioning (#2419)
futa-ikeda Dec 3, 2024
485756a
[ENG-6448] Update preprint-doi component to handle versions (#2420)
futa-ikeda Dec 4, 2024
47ea434
[ENG-6434] Show/hide file upload page on preprint submit workflow (#2…
futa-ikeda Dec 5, 2024
5515ddc
[ENG-6459][ENG-6637][ENG-6638] Add Create new preprint version button…
futa-ikeda Dec 9, 2024
701f265
[ENG-6460] [ENG-6640] Add new preprint version workflow (#2427)
futa-ikeda Dec 26, 2024
6a62967
[ENG-6470] Withdrawn version picker (#2436)
futa-ikeda Dec 30, 2024
58dad21
Add status labels to version dropdowns (#2444)
futa-ikeda Jan 2, 2025
b99fe9f
Add currentVersion param to preprint-doi component (#2448)
futa-ikeda Jan 2, 2025
5877c2f
Add link to version in DOI select (#2449)
futa-ikeda Jan 2, 2025
4355558
Fix bug where link in doi section would not update (#2454)
futa-ikeda Jan 3, 2025
e7f9369
[ENG-6814] Show toast message on 409 error (#2456)
futa-ikeda Jan 6, 2025
ea46239
Merge remote-tracking branch 'upstream/develop' into feature/preprint…
cslzchen Jan 6, 2025
9366206
[ENG-6815][ENG-6843] Update condition to show edit button (#2460)
futa-ikeda Jan 10, 2025
51f651b
Fix bug where preprint status banner does not update (#2465)
futa-ikeda Jan 10, 2025
4a287f6
[ENG-6461] [ENG-6462] Move withdraw button (#2468)
futa-ikeda Jan 14, 2025
a4535b7
[ENG-6931] Update file shown in MFR when changing versions
futa-ikeda Jan 16, 2025
08b0aa5
[ENG-6950] Update withdraw modal language (#2474)
futa-ikeda Jan 17, 2025
a2eb33f
Fix bug to allow any contributor to see preprint rejection comments (…
futa-ikeda Jan 17, 2025
57fcd09
Revert "Fix bug to allow any contributor to see preprint rejection co…
cslzchen Jan 20, 2025
df98061
Reimplement bug fix and account for permissions when accessing prepri…
futa-ikeda Jan 21, 2025
c16996f
Add/update tests (#2483)
futa-ikeda Jan 21, 2025
7e90922
Fix detail page a11y issue (#2487)
futa-ikeda Jan 21, 2025
fae9bdf
Clean up unused/commented code (#2488)
futa-ikeda Jan 22, 2025
f93da22
Merge branch 'develop' into feature/preprints-doi-versioning
futa-ikeda Jan 22, 2025
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
19 changes: 19 additions & 0 deletions app/models/preprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ export enum PreprintPreregLinkInfoEnum {
PREREG_BOTH = 'prereg_both',
}

export const VersionStatusSimpleLabelKey = {
[ReviewsState.PENDING]: 'preprints.detail.version_status.pending',
[ReviewsState.REJECTED]: 'preprints.detail.version_status.rejected',
[ReviewsState.WITHDRAWN]: 'preprints.detail.version_status.withdrawn',
};

export interface PreprintLicenseRecordModel {
copyright_holders: string[];
year: string;
Expand Down Expand Up @@ -70,6 +76,8 @@ export default class PreprintModel extends AbstractNodeModel {
@attr('string') whyNoData!: string | null;
@attr('string') whyNoPrereg!: string | null;
@attr('string') preregLinkInfo!: PreprintPreregLinkInfoEnum;
@attr('number') version!: number;
@attr('boolean') isLatestVersion!: boolean;

@belongsTo('node', { inverse: 'preprints' })
node!: AsyncBelongsTo<NodeModel> & NodeModel;
Expand Down Expand Up @@ -107,6 +115,9 @@ export default class PreprintModel extends AbstractNodeModel {
@hasMany('identifiers')
identifiers!: AsyncHasMany<IdentifierModel>;

@hasMany('preprint', { inverse: null })
versions!: AsyncHasMany<PreprintModel>;

@alias('links.doi') articleDoiUrl!: string | null;
@alias('links.preprint_doi') preprintDoiUrl!: string;

Expand All @@ -123,6 +134,14 @@ export default class PreprintModel extends AbstractNodeModel {
.replace(/({{year}})/g, year)
.replace(/({{copyrightHolders}})/g, copyright_holders.join(', '));
}

get currentUserIsAdmin(): boolean {
return this.currentUserPermissions.includes(Permission.Admin);
}

get canCreateNewVersion(): boolean {
return this.currentUserIsAdmin && this.datePublished && this.isLatestVersion;
}
}

declare module 'ember-data/types/registries/model' {
Expand Down
135 changes: 135 additions & 0 deletions app/preprints/-components/preprint-doi/component-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { click } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { ModelInstance } from 'ember-cli-mirage';

import PreprintProviderModel from 'ember-osf-web/models/preprint-provider';
import PreprintModel from 'ember-osf-web/models/preprint';
import { ReviewsState } from 'ember-osf-web/models/provider';
import { OsfLinkRouterStub } from 'ember-osf-web/tests/integration/helpers/osf-link-router-stub';

module('Integration | Component | preprint-doi', function(hooks) {
setupRenderingTest(hooks);
setupMirage(hooks);

test('it renders', async function(assert) {
this.owner.unregister('service:router');
this.owner.register('service:router', OsfLinkRouterStub);
this.store = this.owner.lookup('service:store');
server.loadFixtures('preprint-providers');
const mirageProvider = server.schema.preprintProviders.find('osf') as ModelInstance<PreprintProviderModel>;
const miragePreprint = server.create('preprint', {
id: 'doied',
provider: mirageProvider,
}, 'withVersions');
// Version 1 has a DOI and has a preprintDoiCreated date
const version1 = server.schema.preprints.find('doied_v1') as ModelInstance<PreprintModel>;
version1.update({ preprintDoiCreated: new Date('2020-02-02') });
// Version 2 has a DOI but no preprintDoiCreated date
const version2 = server.schema.preprints.find('doied_v2') as ModelInstance<PreprintModel>;
version2.update({ preprintDoiCreated: null });
// Version 3 is pending moderator approval and is not published, therefore has no DOI
const version3 = server.schema.preprints.find('doied_v3') as ModelInstance<PreprintModel>;
version3.update({
preprintDoiCreated: null,
isPublished: false,
isPreprintDoi: false, // Mirage flag used to determine if a DOI should be created
});

const preprint = await this.store.findRecord('preprint', miragePreprint.id);
const versions = await preprint.queryHasMany('versions');

const provider = await this.store.findRecord('preprint-provider', mirageProvider.id);
this.set('versions', versions);
this.set('currentVersion', versions[1]);
this.set('provider', provider);

await render(hbs`
<Preprints::-Components::PreprintDoi
@currentVersion={{this.currentVersion}}
@versions={{this.versions}}
@provider={{this.provider}}
/>
`);

// check headings exist
assert.dom('[data-test-preprint-doi-heading]').exists('Preprint DOI heading exists');
assert.dom('[data-test-preprint-doi-heading]').hasText('Preprint DOI', 'Preprint DOI heading has correct text');

// check dropdown exists
assert.dom('[data-test-version-select-dropdown]').exists('Version select dropdown exists');
assert.dom('[data-test-version-select-dropdown]')
.hasText('Version 2 (Rejected)', 'Dropdown has passed in currentVersiom selected by default');
assert.dom('[data-test-preprint-version="2"]').exists('Version 2 is shown');

assert.dom('[data-test-view-version-link]').exists('View in OSF button exists');
assert.dom('[data-test-view-version-link]').hasText('View version 2', 'View version link has correct text');

// check version2 has DOI text
assert.dom('[data-test-no-doi-text]').doesNotExist('No DOI text does not exist');
assert.dom('[data-test-unlinked-doi-url]').exists('Preprint DOI URL exists');
assert.dom('[data-test-unlinked-doi-description]').exists('Preprint DOI description exists');
assert.dom('[data-test-unlinked-doi-description]')
// eslint-disable-next-line max-len
.hasText('DOIs are minted by a third party, and may take up to 24 hours to be registered.', 'Description is correct');

// check version3 has DOI, but no preprintDoiCreated date
await click('[data-test-version-select-dropdown]');
await click('[data-test-preprint-version="3"]');
assert.dom('[data-test-unlinked-doi-url]').doesNotExist('Unlinked preprint DOI URL does not exist');
assert.dom('[data-test-no-doi-text]').exists('No DOI text exists');
assert.dom('[data-test-no-doi-text]').hasText('DOI created after moderator approval', 'No DOI text is correct');

// check version1 has DOI and preprintDoiCreated date
await click('[data-test-version-select-dropdown]');
await click('[data-test-preprint-version="1"]');
assert.dom('[data-test-unlinked-doi-url]').doesNotExist('Unlinked preprint DOI URL does not exist');
assert.dom('[data-test-unlinked-doi-description]').doesNotExist('Unlinked description does not exist');
assert.dom('[data-test-linked-doi-url]').exists('Preprint DOI URL exists');
});

test('it renders statuses', async function(assert) {
this.owner.unregister('service:router');
this.owner.register('service:router', OsfLinkRouterStub);
this.store = this.owner.lookup('service:store');
server.loadFixtures('preprint-providers');
const mirageProvider = server.schema.preprintProviders.find('osf') as ModelInstance<PreprintProviderModel>;
const miragePreprint = server.create('preprint', {
id: 'doied',
provider: mirageProvider,
}, 'withVersions');
const version1 = server.schema.preprints.find('doied_v1') as ModelInstance<PreprintModel>;
version1.update({ reviewsState: ReviewsState.ACCEPTED });
const version2 = server.schema.preprints.find('doied_v2') as ModelInstance<PreprintModel>;
version2.update({ reviewsState: ReviewsState.PENDING });
const version3 = server.schema.preprints.find('doied_v3') as ModelInstance<PreprintModel>;
version3.update({ reviewsState: ReviewsState.WITHDRAWN });

const preprint = await this.store.findRecord('preprint', miragePreprint.id);
const versions = await preprint.queryHasMany('versions');

const provider = await this.store.findRecord('preprint-provider', mirageProvider.id);
this.set('currentVersion', versions[0]);
this.set('versions', versions);
this.set('provider', provider);

await render(hbs`
<Preprints::-Components::PreprintDoi
@currentVersion={{this.currentVersion}}
@versions={{this.versions}}
@provider={{this.provider}}
/>
`);

await click('[data-test-version-select-dropdown]');
assert.dom('[data-test-preprint-version="1"]').exists('Version 1 exists');
assert.dom('[data-test-preprint-version="1"]').hasText('Version 1', 'Version 1 is accepted');
assert.dom('[data-test-preprint-version="2"]').exists('Version 2 exists');
assert.dom('[data-test-preprint-version="2"]').hasText('Version 2 (Pending)', 'Version 2 is pending');
assert.dom('[data-test-preprint-version="3"]').exists('Version 3 exists');
assert.dom('[data-test-preprint-version="3"]').hasText('Version 3 (Withdrawn)', 'Version 3 is withdrawn');
});
});
22 changes: 19 additions & 3 deletions app/preprints/-components/preprint-doi/component.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import Component from '@glimmer/component';
import PreprintModel from 'ember-osf-web/models/preprint';
import { tracked } from '@glimmer/tracking';
import Intl from 'ember-intl/services/intl';

import PreprintModel, { VersionStatusSimpleLabelKey } from 'ember-osf-web/models/preprint';
import PreprintProviderModel from 'ember-osf-web/models/preprint-provider';

interface InputArgs {
preprint: PreprintModel;
versions: PreprintModel[];
provider: PreprintProviderModel;
currentVersion: PreprintModel;
}

export default class PreprintAbstract extends Component<InputArgs> {
@service intl!: Intl;
futa-ikeda marked this conversation as resolved.
Show resolved Hide resolved

provider = this.args.provider;
documentType = this.provider.documentType.singularCapitalized;

@tracked selectedVersion = this.args.currentVersion;

reviewStateLabelKeyMap = VersionStatusSimpleLabelKey;

documentType = this.provider.documentType.singular;
@action
selectVersion(version: PreprintModel) {
this.selectedVersion = version;
}
}
8 changes: 8 additions & 0 deletions app/preprints/-components/preprint-doi/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.version-dropdown {
margin-bottom: 12px;
width: 200px;
}

.current-version {
font-weight: bold;
}
76 changes: 58 additions & 18 deletions app/preprints/-components/preprint-doi/template.hbs
Original file line number Diff line number Diff line change
@@ -1,23 +1,63 @@
<div>
<h4>{{t 'preprints.detail.preprint_doi' documentType=this.documentType}}</h4>
{{#if @preprint.preprintDoiUrl}}
{{#if @preprint.preprintDoiCreated}}
<OsfLink
data-test-article-doi
data-analytics-name='preprint doi'
@href={{@preprint.preprintDoiUrl}}
<h4 data-test-preprint-doi-heading>
{{t 'preprints.detail.preprint_doi' documentType=this.documentType}}
</h4>
{{#if @versions}}
<PowerSelect
data-test-version-select-dropdown
local-class='version-dropdown'
@options={{@versions}}
@selected={{this.selectedVersion}}
@onChange={{this.selectVersion}}
as |version|
>
<span
local-class={{if (eq version.version @currentVersion.version) 'current-version'}}
data-test-preprint-version={{version.version}}
>
{{@preprint.preprintDoiUrl}}
</OsfLink>
{{else}}
<p> {{@preprint.preprintDoiUrl}} </p>
<p> {{t 'preprints.detail.preprint_pending_doi_minted'}} </p>
{{/if}}
{{#let (get this.reviewStateLabelKeyMap version.reviewsState) as |reviewStateLabelKey|}}
{{t 'preprints.detail.version_doi_title' number=version.version}}
{{#if reviewStateLabelKey}}
{{t (get this.reviewStateLabelKeyMap version.reviewsState)}}
{{/if}}
{{/let}}
</span>
</PowerSelect>
{{else}}
{{#if (not @preprint.public)}}
{{t 'preprints.detail.preprint_pending_doi' documentType=this.documentType}}
{{else if (and this.provider.reviewsWorkflow (not this.preprint.isPublished))}}
{{t 'preprints.detail.preprint_pending_doi_moderation'}}
<p>{{t 'preprints.detail.no_versions'}}</p>
{{/if}}
{{#if this.selectedVersion}}
<OsfLink
data-test-view-version-link
data-analytics-name='View version link'
@route='preprints.detail'
@models={{array @provider.id this.selectedVersion.id}}
>
{{t 'preprints.detail.view_version' number=this.selectedVersion.version}}
</OsfLink>
{{#if this.selectedVersion.preprintDoiUrl}}
{{#if this.selectedVersion.preprintDoiCreated}}
<OsfLink
data-test-linked-doi-url
data-analytics-name='preprint doi'
@href={{this.selectedVersion.preprintDoiUrl}}
>
{{this.selectedVersion.preprintDoiUrl}}
</OsfLink>
{{else}}
<p data-test-unlinked-doi-url> {{this.selectedVersion.preprintDoiUrl}} </p>
<p data-test-unlinked-doi-description> {{t 'preprints.detail.preprint_pending_doi_minted'}} </p>
{{/if}}
{{else}}
<p data-test-no-doi-text>
{{#if (not this.selectedVersion.public)}}
{{t 'preprints.detail.preprint_pending_doi' documentType=this.documentType}}
{{else if (and this.provider.reviewsWorkflow (not this.preprint.isPublished))}}
{{t 'preprints.detail.preprint_pending_doi_moderation'}}
{{else}}
{{t 'preprints.detail.no_doi'}}
{{/if}}
</p>
{{/if}}
{{/if}}
</div>
</div>
31 changes: 14 additions & 17 deletions app/preprints/-components/preprint-file-render/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import Media from 'ember-responsive';


interface InputArgs {
preprint: PreprintModel;
provider: PreprintProviderModel;
primaryFile: FileModel;
preprint: PreprintModel;
provider: PreprintProviderModel;
primaryFile: FileModel;
}

export interface VersionModel extends FileVersionModel {
downloadUrl?: string;
downloadUrl?: string;
}

export default class PreprintFileRender extends Component<InputArgs> {
Expand All @@ -30,42 +30,39 @@ export default class PreprintFileRender extends Component<InputArgs> {
@tracked primaryFileHasVersions = false;
@tracked fileVersions: VersionModel[] = [];

primaryFile = this.args.primaryFile;
provider = this.args.provider;
preprint = this.args.preprint;

constructor(owner: unknown, args: InputArgs) {
super(owner, args);

taskFor(this.loadPrimaryFileVersions).perform();

this.allowCommenting = this.provider.allowCommenting && this.preprint.isPublished && this.preprint.public;
this.allowCommenting = this.args.provider.allowCommenting
&& this.args.preprint.isPublished && this.args.preprint.public;
}

@task
@waitFor
private async loadPrimaryFileVersions() {
const primaryFileVersions = (await this.primaryFile.queryHasMany('versions', {
const primaryFileVersions = (await this.args.primaryFile.queryHasMany('versions', {
sort: '-id', 'page[size]': 50,
})).toArray();
this.serializeVersions(primaryFileVersions);
this.primaryFileHasVersions = primaryFileVersions.length > 0;
}

private serializeVersions(versions: FileVersionModel[]) {
const downloadUrl = this.primaryFile.links.download as string || '';
const downloadUrl = this.args.primaryFile.links.download as string || '';

versions.map((version: VersionModel) => {
const dateFormatted = encodeURIComponent(version.dateCreated.toISOString());
const displayName = version.name.replace(/(\.\w+)?$/, ext => `-${dateFormatted}${ext}`);

this.fileVersions.push(
{
name: version.name,
id: version.id,
dateCreated: version.dateCreated,
downloadUrl: `${downloadUrl}?version=${version.id}&displayName=${displayName}`,
} as VersionModel,
{
name: version.name,
id: version.id,
dateCreated: version.dateCreated,
downloadUrl: `${downloadUrl}?version=${version.id}&displayName=${displayName}`,
} as VersionModel,
);
return version;
});
Expand Down
Loading
Loading