Skip to content

Commit 47763b9

Browse files
committed
Merge remote-tracking branch 'upstream/develop' into feature/verified-resource-linking
2 parents eefee2b + 186fd80 commit 47763b9

File tree

33 files changed

+593
-18
lines changed

33 files changed

+593
-18
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## [25.12.0] - 2025-06-13
8+
### Added
9+
- Google File Picker workflow
10+
- Misc. improvements
11+
12+
## [25.11.0] - 2025-06-11
13+
### Added
14+
- Manual GUID and DOI assignment during Preprint and Registration Creation
15+
716
## [25.10.0] - 2025-05-13
817
### Added
918
- Re-enable view and download counts for preprints

app/config/environment.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ declare const config: {
141141
doiUrlPrefix: string;
142142
dataciteTrackerRepoId: string;
143143
dataCiteTrackerUrl: string;
144+
googleFilePicker: {
145+
GOOGLE_FILE_PICKER_SCOPES: string;
146+
GOOGLE_FILE_PICKER_CLIENT_ID: string;
147+
GOOGLE_FILE_PICKER_API_KEY: string;
148+
GOOGLE_FILE_PICKER_APP_ID: number;
149+
}
144150
};
145151
social: {
146152
twitter: {

app/guid-node/files/provider/template.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
>
88
<div local-class='FileBrowser'>
99
<FileBrowser
10+
@configuredStorageAddon={{this.model.configuredStorageAddon}}
1011
@manager={{manager}}
1112
@selectable={{not this.model.providerTask.value.currentUser.viewOnlyToken}}
1213
@enableUpload={{true}}

app/models/authorized-account.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Model, { attr } from '@ember-data/model';
1+
import Model, { attr} from '@ember-data/model';
22
import { OperationKwargs } from 'ember-osf-web/models/addon-operation-invocation';
33

44
export enum ConnectedCapabilities {
@@ -36,7 +36,6 @@ export default class AuthorizedAccountModel extends Model {
3636
return;
3737
}
3838

39-
4039
async getItemInfo(this: AuthorizedAccountModel, _itemId: string) : Promise<any> {
4140
// To be implemented in child classes
4241
return;

app/models/authorized-storage-account.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import { AsyncBelongsTo, belongsTo } from '@ember-data/model';
1+
import { AsyncBelongsTo, attr, belongsTo } from '@ember-data/model';
22
import { waitFor } from '@ember/test-waiters';
33
import { task } from 'ember-concurrency';
44
import { ConnectedStorageOperationNames, OperationKwargs } from 'ember-osf-web/models/addon-operation-invocation';
5+
import ExternalStorageServiceModel from 'ember-osf-web/models/external-storage-service';
56

6-
import ExternalStorageServiceModel from './external-storage-service';
77
import AuthorizedAccountModel from './authorized-account';
88
import UserReferenceModel from './user-reference';
99

1010
export default class AuthorizedStorageAccountModel extends AuthorizedAccountModel {
11+
@attr('fixstring') serializeOauthToken!: string;
12+
@attr('fixstring') oauthToken!: string;
13+
1114
@belongsTo('user-reference', { inverse: 'authorizedStorageAccounts' })
1215
readonly accountOwner!: AsyncBelongsTo<UserReferenceModel> & UserReferenceModel;
1316

app/models/index-card-search.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export interface SearchFilter {
1010
filterType?: string;
1111
}
1212

13-
export const ShareMoreThanTenThousand = 'https://share.osf.io/vocab/2023/trove/ten-thousands-and-more';
13+
export const ShareMoreThanTenThousand = 'trove:ten-thousands-and-more';
1414

1515
export default class IndexCardSearchModel extends Model {
1616
@attr('string') cardSearchText!: string;

app/models/preprint.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ export default class PreprintModel extends AbstractNodeModel {
8282
@attr('string') preregLinkInfo!: PreprintPreregLinkInfoEnum;
8383
@attr('number') version!: number;
8484
@attr('boolean') isLatestVersion!: boolean;
85+
@attr('string') manualDoi!: string;
86+
@attr('string') manualGuid!: string;
8587

8688
@belongsTo('node', { inverse: 'preprints' })
8789
node!: AsyncBelongsTo<NodeModel> & NodeModel;

app/models/registration.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ export default class RegistrationModel extends NodeModel.extend(Validations) {
115115
@attr('boolean') hasAnalyticCode!: boolean;
116116
@attr('boolean') hasPapers!: boolean;
117117
@attr('boolean') hasSupplements!: boolean;
118+
@attr('string') manualDoi!: string;
119+
@attr('string') manualGuid!: string;
118120

119121
// Write-only attributes
120122
@attr('array') includedNodeIds?: string[];

app/models/search-result.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ export default class SearchResultModel extends Model {
314314
}
315315

316316
get isWithdrawn() {
317-
return this.resourceMetadata.dateWithdrawn || this.resourceMetadata['https://osf.io/vocab/2022/withdrawal'];
317+
return this.resourceMetadata.dateWithdrawn || this.resourceMetadata['osf:withdrawal'];
318318
}
319319

320320
get configuredAddonNames() {

app/packages/addons-service/provider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ export default class Provider {
258258
accountOwner: this.userReference,
259259
});
260260
await newAccount.save();
261+
newAccount.initiateOauth = null;
261262
return newAccount;
262263
}
263264

app/preprints/-components/submit/title-and-abstract/component.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import Component from '@glimmer/component';
22
import PreprintStateMachine from 'ember-osf-web/preprints/-components/submit/preprint-state-machine/component';
33
import { action } from '@ember/object';
44
import { ValidationObject } from 'ember-changeset-validations';
5-
import { validatePresence, validateLength } from 'ember-changeset-validations/validators';
5+
import { validatePresence, validateLength, validateFormat } from 'ember-changeset-validations/validators';
66
import buildChangeset from 'ember-osf-web/utils/build-changeset';
77
import { inject as service } from '@ember/service';
88
import Intl from 'ember-intl/services/intl';
9+
import { DOIRegex } from 'ember-osf-web/utils/doi';
910

1011
/**
1112
* The TitleAndAbstract Args
@@ -17,6 +18,8 @@ interface TitleAndAbstractArgs {
1718
interface TitleAndAbstractForm {
1819
title: string;
1920
description: string;
21+
manualDoi: string;
22+
manualGuid: string;
2023
}
2124

2225
/**
@@ -45,6 +48,22 @@ export default class TitleAndAbstract extends Component<TitleAndAbstractArgs>{
4548
},
4649
}),
4750
],
51+
manualDoi: validateFormat({
52+
allowBlank: true,
53+
allowNone: true,
54+
ignoreBlank: true,
55+
regex: DOIRegex,
56+
type: 'invalid_doi',
57+
}),
58+
manualGuid: validateLength({
59+
allowBlank: true,
60+
min:5,
61+
type: 'greaterThanOrEqualTo',
62+
translationArgs: {
63+
description: this.intl.t('preprints.submit.step-title.guid'),
64+
gte: '5 characters',
65+
},
66+
}),
4867
};
4968

5069
titleAndAbstractFormChangeset = buildChangeset(this.args.manager.preprint, this.titleAndAbstractFormValidation);

app/preprints/-components/submit/title-and-abstract/template.hbs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,42 @@
4848
@onKeyUp={{this.validate}}
4949
/>
5050
{{/let}}
51+
{{#if (and (feature-flag 'manual_doi_and_guid') (not @manager.isEditFlow))}}
52+
{{#let (unique-id 'manualDoi') as |manualDoiField|}}
53+
<label for={{manualDoiField}}
54+
data-test-manual-doi-label
55+
>
56+
{{t 'preprints.submit.step-title.doi'}}
57+
<span local-class='required'>*</span>
58+
</label>
59+
<form.text
60+
data-test-manual-doi-input
61+
@valuePath={{'manualDoi'}}
62+
@isRequired={{false}}
63+
{{on 'change' this.validate}}
64+
local-class='input-container'
65+
@uniqueID={{manualDoiField}}
66+
@onKeyUp={{this.validate}}
67+
/>
68+
{{/let}}
69+
{{#let (unique-id 'manualGuid') as |manualGuidField|}}
70+
<label for={{manualGuidField}}
71+
data-test-manudal-guid-label
72+
>
73+
{{t 'preprints.submit.step-title.guid'}}
74+
<span local-class='required'>*</span>
75+
</label>
76+
<form.text
77+
data-test-manual-guid-input
78+
@valuePath={{'manualGuid'}}
79+
@isRequired={{false}}
80+
{{on 'change' this.validate}}
81+
local-class='input-container'
82+
@uniqueID={{manualGuidField}}
83+
@onKeyUp={{this.validate}}
84+
/>
85+
{{/let}}
86+
{{/if}}
5187
</FormControls>
5288
</div>
5389
</div>

config/environment.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,13 @@ const {
102102
SHARE_SEARCH_URL: shareSearchUrl = 'http://localhost:8003/api/v2/search/creativeworks/_search',
103103
SOURCEMAPS_ENABLED: sourcemapsEnabled = true,
104104
SHOW_DEV_BANNER = false,
105+
106+
GOOGLE_FILE_PICKER_SCOPES,
107+
/* eslint-disable-next-line max-len */
108+
GOOGLE_FILE_PICKER_CLIENT_ID,
109+
GOOGLE_FILE_PICKER_API_KEY,
110+
GOOGLE_FILE_PICKER_APP_ID,
111+
105112
} = { ...process.env, ...localConfig };
106113

107114
module.exports = function(environment) {
@@ -224,6 +231,12 @@ module.exports = function(environment) {
224231
doiUrlPrefix: 'https://doi.org/',
225232
dataciteTrackerRepoId,
226233
dataCiteTrackerUrl,
234+
googleFilePicker: {
235+
GOOGLE_FILE_PICKER_SCOPES,
236+
GOOGLE_FILE_PICKER_CLIENT_ID,
237+
GOOGLE_FILE_PICKER_API_KEY,
238+
GOOGLE_FILE_PICKER_APP_ID,
239+
},
227240
},
228241
social: {
229242
twitter: {
@@ -316,6 +329,7 @@ module.exports = function(environment) {
316329
},
317330
storageI18n: 'storage_i18n',
318331
gravyWaffle: 'gravy_waffle',
332+
manualDoiAndGuid: 'manual_doi_and_guid',
319333
enableInactiveSchemas: 'enable_inactive_schemas',
320334
verifyEmailModals: 'ember_verify_email_modals',
321335
ABTesting: {

lib/osf-components/addon/components/activity-log/component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export default class ActivityLogComponent extends Component {
44
public loadEmbeds = {
55
embed:
66
[
7-
'group', 'linked_node', 'linked_registration', 'original_node',
7+
'linked_node', 'linked_registration', 'original_node',
88
'template_node', 'user',
99
],
1010
};

lib/osf-components/addon/components/addons-service/configured-addon-edit/component.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { action } from '@ember/object';
2+
import { waitFor } from '@ember/test-waiters';
23
import Component from '@glimmer/component';
34
import { tracked } from '@glimmer/tracking';
4-
import { TaskInstance } from 'ember-concurrency';
5+
import { task, TaskInstance } from 'ember-concurrency';
6+
import { taskFor } from 'ember-concurrency-ts';
7+
58

69
import { Item, ItemType } from 'ember-osf-web/models/addon-operation-invocation';
710
import AuthorizedAccountModel from 'ember-osf-web/models/authorized-account';
@@ -14,6 +17,7 @@ import ConfiguredCitationAddonModel from 'ember-osf-web/models/configured-citati
1417
import ConfiguredComputingAddonModel from 'ember-osf-web/models/configured-computing-addon';
1518
import ConfiguredLinkAddonModel from 'ember-osf-web/models/configured-link-addon';
1619
import ConfiguredStorageAddonModel from 'ember-osf-web/models/configured-storage-addon';
20+
import ExternalStorageServiceModel from 'ember-osf-web/models/external-storage-service';
1721

1822

1923
interface Args {
@@ -30,6 +34,9 @@ export default class ConfiguredAddonEdit extends Component<Args> {
3034
@tracked selectedItem = '';
3135
@tracked selectedItemDisplayName = '';
3236
@tracked selectedResourceType = '';
37+
@tracked isWBGoogleDrive = false;
38+
@tracked accountId!: string;
39+
3340
originalName = this.displayName;
3441
originalRootFolder = this.selectedFolder;
3542
originalSelectedItem = this.selectedItem;
@@ -41,6 +48,7 @@ export default class ConfiguredAddonEdit extends Component<Args> {
4148
super(owner, args);
4249
if (this.args.configuredAddon) {
4350
if (this.args.configuredAddon instanceof ConfiguredStorageAddonModel) {
51+
taskFor(this.loadExternalStorageService).perform();
4452
this.defaultKwargs['itemType'] = ItemType.Folder;
4553
}
4654
if (this.args.configuredAddon instanceof ConfiguredCitationAddonModel) {
@@ -54,6 +62,7 @@ export default class ConfiguredAddonEdit extends Component<Args> {
5462
}
5563
if (this.args.authorizedAccount) {
5664
if (this.args.authorizedAccount instanceof AuthorizedStorageAccountModel) {
65+
taskFor(this.loadExternalStorageService).perform();
5766
this.defaultKwargs['itemType'] = ItemType.Folder;
5867
}
5968
if (this.args.authorizedAccount instanceof AuthorizedCitationAccountModel) {
@@ -71,13 +80,43 @@ export default class ConfiguredAddonEdit extends Component<Args> {
7180
}
7281

7382
get requiresFilesWidget() {
83+
/**
84+
* This is called only to authorize because the current implementation will throw an
85+
* error because the "root folder" is not yet set.
86+
*/
7487
return !(
7588
this.args.authorizedAccount instanceof AuthorizedComputingAccountModel
7689
||
7790
this.args.configuredAddon instanceof ConfiguredComputingAddonModel
7891
);
7992
}
8093

94+
@task
95+
@waitFor
96+
async loadExternalStorageService() {
97+
let external!: ExternalStorageServiceModel;
98+
if (this.args.configuredAddon && this.args.configuredAddon instanceof ConfiguredStorageAddonModel) {
99+
const baseAccount = await this.args.configuredAddon.baseAccount;
100+
this.accountId = baseAccount?.id;
101+
external = await this.args.configuredAddon.externalStorageService;
102+
}
103+
if (this.args.authorizedAccount && this.args.authorizedAccount instanceof AuthorizedStorageAccountModel) {
104+
external = await this.args.authorizedAccount.externalStorageService;
105+
106+
this.accountId = this.args.authorizedAccount.id;
107+
}
108+
109+
this.isWBGoogleDrive = external?.wbKey === 'googledrive';
110+
}
111+
112+
get isGoogleDrive(): boolean {
113+
return this.isWBGoogleDrive;
114+
}
115+
116+
get displayFileManager(): boolean {
117+
return this.requiresFilesWidget && !this.isGoogleDrive;
118+
}
119+
81120
get invalidDisplayName() {
82121
return !this.displayName || this.displayName?.trim().length === 0;
83122
}

lib/osf-components/addon/components/addons-service/configured-addon-edit/styles.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,8 @@
6363
.item-name {
6464
white-space: normal;
6565
}
66+
67+
.picker-style {
68+
white-space: pre-wrap;
69+
70+
}

lib/osf-components/addon/components/addons-service/configured-addon-edit/template.hbs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
</span>
2222
{{/if}}
2323
</div>
24-
{{#if this.requiresFilesWidget }}
24+
{{#if this.displayFileManager}}
2525
<div local-class='input-wrapper'>
2626
<b>
2727
{{#if this.isLinkAddon}}
@@ -158,6 +158,13 @@
158158
</tbody>
159159
</table>
160160
</AddonsService::FileManager>
161+
{{else}}
162+
<GoogleFilePickerWidget
163+
@selectedFolderName={{this.selectedFolderDisplayName}}
164+
@selectFolder={{action this.selectFolder}}
165+
@isFolderPicker={{true}}
166+
@accountId={{this.accountId}}
167+
></GoogleFilePickerWidget>
161168
{{/if}}
162169
{{#if this.isLinkAddon}}
163170
<label>

lib/osf-components/addon/components/addons-service/manager/component.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,8 @@ export default class AddonsServiceManagerComponent extends Component<Args> {
235235
@waitFor
236236
async createAuthorizedAccount(arg: AccountCreationArgs) {
237237
if (this.selectedProvider) {
238-
const newAccount = await taskFor(this.selectedProvider.createAuthorizedAccount)
238+
return await taskFor(this.selectedProvider.createAuthorizedAccount)
239239
.perform(arg);
240-
return newAccount;
241240
}
242241
return undefined;
243242
}

0 commit comments

Comments
 (0)