Skip to content

Commit 74a7e76

Browse files
committed
Merge branch 'release/25.12.0'
2 parents c573887 + 14c69fb commit 74a7e76

File tree

25 files changed

+443
-16
lines changed

25 files changed

+443
-16
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ 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+
712
## [25.11.0] - 2025-06-11
813
### Added
914
- Manual GUID and DOI assignment during Preprint and Registration Creation

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/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
@@ -237,6 +237,7 @@ export default class Provider {
237237
accountOwner: this.userReference,
238238
});
239239
await newAccount.save();
240+
newAccount.initiateOauth = null;
240241
return newAccount;
241242
}
242243

config/environment.js

Lines changed: 13 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: {

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: 39 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';
@@ -12,6 +15,7 @@ import ConfiguredAddonModel from 'ember-osf-web/models/configured-addon';
1215
import ConfiguredCitationAddonModel from 'ember-osf-web/models/configured-citation-addon';
1316
import ConfiguredComputingAddonModel from 'ember-osf-web/models/configured-computing-addon';
1417
import ConfiguredStorageAddonModel from 'ember-osf-web/models/configured-storage-addon';
18+
import ExternalStorageServiceModel from 'ember-osf-web/models/external-storage-service';
1519

1620

1721
interface Args {
@@ -25,6 +29,8 @@ export default class ConfiguredAddonEdit extends Component<Args> {
2529
@tracked selectedFolder = this.args.configuredAddon?.rootFolder;
2630
@tracked selectedFolderDisplayName = this.args.configuredAddon?.rootFolderName;
2731
@tracked currentItems: Item[] = [];
32+
@tracked isWBGoogleDrive = false;
33+
@tracked accountId!: string;
2834

2935
originalName = this.displayName;
3036
originalRootFolder = this.selectedFolder;
@@ -34,6 +40,7 @@ export default class ConfiguredAddonEdit extends Component<Args> {
3440
super(owner, args);
3541
if (this.args.configuredAddon) {
3642
if (this.args.configuredAddon instanceof ConfiguredStorageAddonModel) {
43+
taskFor(this.loadExternalStorageService).perform();
3744
this.defaultKwargs['itemType'] = ItemType.Folder;
3845
}
3946
if (this.args.configuredAddon instanceof ConfiguredCitationAddonModel) {
@@ -42,6 +49,7 @@ export default class ConfiguredAddonEdit extends Component<Args> {
4249
}
4350
if (this.args.authorizedAccount) {
4451
if (this.args.authorizedAccount instanceof AuthorizedStorageAccountModel) {
52+
taskFor(this.loadExternalStorageService).perform();
4553
this.defaultKwargs['itemType'] = ItemType.Folder;
4654
}
4755
if (this.args.authorizedAccount instanceof AuthorizedCitationAccountModel) {
@@ -50,6 +58,28 @@ export default class ConfiguredAddonEdit extends Component<Args> {
5058
}
5159
}
5260

61+
/**
62+
* This is called only to authorize because the current implementation will throw an
63+
* error because the "root folder" is not yet set.
64+
*/
65+
@task
66+
@waitFor
67+
async loadExternalStorageService() {
68+
let external!: ExternalStorageServiceModel;
69+
if (this.args.configuredAddon && this.args.configuredAddon instanceof ConfiguredStorageAddonModel) {
70+
const baseAccount = await this.args.configuredAddon.baseAccount;
71+
this.accountId = baseAccount?.id;
72+
external = await this.args.configuredAddon.externalStorageService;
73+
}
74+
if (this.args.authorizedAccount && this.args.authorizedAccount instanceof AuthorizedStorageAccountModel) {
75+
external = await this.args.authorizedAccount.externalStorageService;
76+
77+
this.accountId = this.args.authorizedAccount.id;
78+
}
79+
80+
this.isWBGoogleDrive = external?.wbKey === 'googledrive';
81+
}
82+
5383
get requiresRootFolder() {
5484
return !(
5585
this.args.authorizedAccount instanceof AuthorizedComputingAccountModel
@@ -58,6 +88,14 @@ export default class ConfiguredAddonEdit extends Component<Args> {
5888
);
5989
}
6090

91+
get isGoogleDrive(): boolean {
92+
return this.isWBGoogleDrive;
93+
}
94+
95+
get displayFileManager(): boolean {
96+
return this.requiresRootFolder && !this.isGoogleDrive;
97+
}
98+
6199
get invalidDisplayName() {
62100
return !this.displayName || this.displayName?.trim().length === 0;
63101
}

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
@@ -62,3 +62,8 @@
6262
.item-name {
6363
white-space: normal;
6464
}
65+
66+
.picker-style {
67+
white-space: pre-wrap;
68+
69+
}

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.requiresRootFolder }}
24+
{{#if this.displayFileManager}}
2525
<div local-class='input-wrapper'>
2626
<b>
2727
{{t 'addons.configure.selected-folder'}}
@@ -150,6 +150,13 @@
150150
</tbody>
151151
</table>
152152
</AddonsService::FileManager>
153+
{{else}}
154+
<GoogleFilePickerWidget
155+
@selectedFolderName={{this.selectedFolderDisplayName}}
156+
@selectFolder={{action this.selectFolder}}
157+
@isFolderPicker={{true}}
158+
@accountId={{this.accountId}}
159+
></GoogleFilePickerWidget>
153160
{{/if}}
154161
<div local-class='footer-buttons-wrapper'>
155162
<Button

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,8 @@ export default class AddonsServiceManagerComponent extends Component<Args> {
225225
@waitFor
226226
async createAuthorizedAccount(arg: AccountCreationArgs) {
227227
if (this.selectedProvider) {
228-
const newAccount = await taskFor(this.selectedProvider.createAuthorizedAccount)
228+
return await taskFor(this.selectedProvider.createAuthorizedAccount)
229229
.perform(arg);
230-
return newAccount;
231230
}
232231
return undefined;
233232
}
Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,65 @@
11
// import { inject as service } from '@ember/service';
2+
import { action } from '@ember/object';
3+
import { waitFor } from '@ember/test-waiters';
24
import Component from '@glimmer/component';
35
import { tracked } from '@glimmer/tracking';
4-
6+
import { task } from 'ember-concurrency';
7+
import { taskFor } from 'ember-concurrency-ts';
8+
import ConfiguredStorageAddonModel from 'ember-osf-web/models/configured-storage-addon';
9+
import GoogleFilePickerWidget from 'osf-components/components/google-file-picker-widget/component';
510
import StorageManager from 'osf-components/components/storage-provider-manager/storage-manager/component';
611

712
interface Args {
813
manager: StorageManager;
14+
configuredStorageAddon: ConfiguredStorageAddonModel;
915
}
1016

1117
export default class FileBrowser extends Component<Args> {
1218
@tracked createFolderModalOpen = false;
19+
@tracked googlePickerComponent: GoogleFilePickerWidget | null = null;
1320
@tracked dropzoneClickableElementId = '';
21+
@tracked isWBGoogleDrive = false;
22+
@tracked googleFilePickerRootFolder!: string;
23+
@tracked accountId!: string;
24+
25+
constructor(owner: unknown, args: Args) {
26+
super(owner, args);
27+
28+
taskFor(this.loadExternalStorageService).perform();
29+
}
30+
31+
/**
32+
* This is called only to authorize because the current implementation will throw an
33+
* error because the "root folder" is not yet set.
34+
*/
35+
@task
36+
@waitFor
37+
async loadExternalStorageService() {
38+
this.googleFilePickerRootFolder = this.args.configuredStorageAddon?.rootFolder;
39+
const baseAccount = await this.args.configuredStorageAddon?.baseAccount;
40+
this.accountId = baseAccount?.id;
41+
const external = await this.args.configuredStorageAddon?.externalStorageService;
42+
this.isWBGoogleDrive = external?.wbKey === 'googledrive';
43+
}
44+
45+
get isGoogleDrive(): boolean {
46+
return this.isWBGoogleDrive;
47+
}
48+
49+
get isGoogleAuthorized(): boolean {
50+
return this.googlePickerComponent?.isGFPDisabled || false;
51+
}
52+
53+
@action
54+
registerChild(child: GoogleFilePickerWidget) {
55+
this.googlePickerComponent = child; // Store the child's instance
56+
}
57+
58+
@action
59+
openGoogleFilePicker(dropdown: any) {
60+
dropdown.close();
61+
if (this.googlePickerComponent) {
62+
this.googlePickerComponent.createPicker();
63+
}
64+
}
1465
}

lib/osf-components/addon/components/file-browser/add-new/template.hbs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,27 @@
3434
>
3535
{{t 'osf-components.file-browser.upload_file'}}
3636
</Button>
37+
{{#if this.isGoogleDrive}}
38+
<Button
39+
data-analytics-name='Add a file from Google Drive'
40+
data-test-add-google-drive
41+
@layout='fake-link'
42+
{{on 'click' (fn this.openGoogleFilePicker dropdown)}}
43+
disabled={{this.isGoogleAuthorized}}
44+
>
45+
{{t 'osf-components.file-browser.add-from-drive'}}
46+
</Button>
47+
{{/if}}
3748
</dropdown.content>
3849
</ResponsiveDropdown>
3950
<FileBrowser::AddNew::CreateFolderModal @isOpen={{this.createFolderModalOpen}} @manager={{@manager}} />
51+
{{#if this.isGoogleDrive}}
52+
<GoogleFilePickerWidget
53+
@isFolderPicker={{false}}
54+
@accountId={{this.accountId}}
55+
@onRegisterChild={{this.registerChild}}
56+
@rootFolderId={{this.googleFilePickerRootFolder}}
57+
@manager={{@manager}}
58+
></GoogleFilePickerWidget>
59+
{{/if}}
4060
{{/let}}

lib/osf-components/addon/components/file-browser/template.hbs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,11 @@
160160
@isRegistration={{@manager.targetNode.isRegistration}}
161161
/>
162162
{{#if (and @manager.currentFolder.userCanUploadToHere @enableUpload)}}
163-
<FileBrowser::AddNew @manager={{@manager}} @setClickableElementId={{uploadWidget.setClickableElementId}} />
163+
<FileBrowser::AddNew
164+
@manager={{@manager}}
165+
@setClickableElementId={{uploadWidget.setClickableElementId}}
166+
@configuredStorageAddon={{@configuredStorageAddon}}
167+
/>
164168
{{/if}}
165169
{{/if}}
166170
</div>

0 commit comments

Comments
 (0)