Skip to content

Commit 56e8128

Browse files
committed
Merge branch 'release/23.14.0'
2 parents 6814ce3 + 45a9766 commit 56e8128

File tree

20 files changed

+330
-4
lines changed

20 files changed

+330
-4
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ 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+
## [23.14.0] - 2023-11-10
8+
### Added
9+
- BOA addon
10+
711
## [23.13.0] - 2023-10-25
812
### Added
913
- Search improvement post release fixes
@@ -1954,6 +1958,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
19541958
### Added
19551959
- Quick Files
19561960

1961+
[23.14.0]: https://github.com/CenterForOpenScience/ember-osf-web/releases/tag/23.14.0
19571962
[23.13.0]: https://github.com/CenterForOpenScience/ember-osf-web/releases/tag/23.13.0
19581963
[23.12.1]: https://github.com/CenterForOpenScience/ember-osf-web/releases/tag/23.12.1
19591964
[23.12.0]: https://github.com/CenterForOpenScience/ember-osf-web/releases/tag/23.12.0

app/guid-file/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export default class GuidFile extends Route {
5858
};
5959
this.set('headTags', this.metaTags.getHeadTags(metaTagsData));
6060
this.headTagsService.collectHeadTags();
61+
await taskFor(model.target.get('getEnabledAddons')).perform();
6162
blocker.done();
6263
}
6364

app/guid-file/template.hbs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@
2121
({{t 'general.version'}}: {{this.viewedVersion}})
2222
{{/if}}
2323
</h2>
24-
<FileActionsMenu @item={{this.model}} @onDelete={{this.onDelete}} @allowRename={{false}} />
24+
<FileActionsMenu
25+
@item={{this.model}}
26+
@onDelete={{this.onDelete}}
27+
@allowRename={{false}}
28+
@addonsEnabled={{this.model.fileModel.target.addonsEnabled}}
29+
/>
2530
</div>
2631
</:header>
2732
<:body>

app/guid-node/files/provider/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export default class GuidNodeFilesProviderRoute extends Route.extend({}) {
1919
@waitFor
2020
async fileProviderTask(guidRouteModel: GuidRouteModel<NodeModel>, fileProviderId: string) {
2121
const node = await guidRouteModel.taskInstance;
22+
await taskFor(node.getEnabledAddons).perform();
2223
try {
2324
const fileProviders = await node.queryHasMany(
2425
'files',

app/models/node.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@ import { computed } from '@ember/object';
44
import { alias, bool, equal, not } from '@ember/object/computed';
55
import { inject as service } from '@ember/service';
66
import { htmlSafe } from '@ember/template';
7+
import { tracked } from '@glimmer/tracking';
78
import { buildValidations, validator } from 'ember-cp-validations';
89
import Intl from 'ember-intl/services/intl';
910

11+
import config from 'ember-osf-web/config/environment';
12+
import { task } from 'ember-concurrency';
13+
import { waitFor } from '@ember/test-waiters';
14+
1015
import getRelatedHref from 'ember-osf-web/utils/get-related-href';
1116

1217
import AbstractNodeModel from 'ember-osf-web/models/abstract-node';
@@ -25,6 +30,13 @@ import RegistrationModel from './registration';
2530
import SubjectModel from './subject';
2631
import WikiModel from './wiki';
2732

33+
const {
34+
OSF: {
35+
apiUrl,
36+
apiNamespace,
37+
},
38+
} = config;
39+
2840
const Validations = buildValidations({
2941
title: [
3042
validator('presence', true),
@@ -108,6 +120,10 @@ export default class NodeModel extends AbstractNodeModel.extend(Validations, Col
108120
@attr('boolean') currentUserCanComment!: boolean;
109121
@attr('boolean') wikiEnabled!: boolean;
110122

123+
// FE-only property to check enabled addons.
124+
// null until getEnabledAddons has been called
125+
@tracked addonsEnabled?: string[];
126+
111127
@hasMany('contributor', { inverse: 'node' })
112128
contributors!: AsyncHasMany<ContributorModel> & ContributorModel[];
113129

@@ -311,6 +327,26 @@ export default class NodeModel extends AbstractNodeModel.extend(Validations, Col
311327

312328
this.set('nodeLicense', props);
313329
}
330+
331+
@task
332+
@waitFor
333+
async getEnabledAddons() {
334+
const endpoint = `${apiUrl}/${apiNamespace}/nodes/${this.id}/addons/`;
335+
const response = await this.currentUser.authenticatedAJAX({
336+
url: endpoint,
337+
type: 'GET',
338+
headers: {
339+
'Content-Type': 'application/json',
340+
},
341+
xhrFields: { withCredentials: true },
342+
});
343+
if (response.data) {
344+
const addonList = response.data
345+
.filter((addon: any) => addon.attributes.node_has_auth)
346+
.map((addon: any) => addon.id);
347+
this.set('addonsEnabled', addonList);
348+
}
349+
}
314350
}
315351

316352
declare module 'ember-data/types/registries/model' {

app/packages/files/file.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ export default abstract class File {
154154
);
155155
}
156156

157+
get isBoaFile() {
158+
return this.fileModel.name.endsWith('.boa');
159+
}
160+
161+
get providerIsOsfstorage() {
162+
return this.fileModel.provider === 'osfstorage';
163+
}
164+
157165
async createFolder(newFolderName: string) {
158166
if (this.fileModel.isFolder) {
159167
await this.fileModel.createFolder(newFolderName);

lib/osf-components/addon/components/file-actions-menu/component.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import { inject as service } from '@ember/service';
55
import Media from 'ember-responsive';
66
import File from 'ember-osf-web/packages/files/file';
77
import StorageManager from 'osf-components/components/storage-provider-manager/storage-manager/component';
8+
import NodeModel from 'ember-osf-web/models/node';
89

910
interface Args {
1011
item: File;
1112
onDelete: () => void;
1213
manager?: StorageManager; // No manager for file-detail page
14+
addonsEnabled? : string[];
1315
}
1416

1517
export default class FileActionsMenu extends Component<Args> {
@@ -19,6 +21,7 @@ export default class FileActionsMenu extends Component<Args> {
1921
@tracked moveModalOpen = false;
2022
@tracked useCopyModal = false;
2123
@tracked renameModalOpen = false;
24+
@tracked isSubmitToBoaModalOpen = false;
2225

2326
@action
2427
closeDeleteModal() {
@@ -34,4 +37,34 @@ export default class FileActionsMenu extends Component<Args> {
3437
openRenameModal() {
3538
this.renameModalOpen = true;
3639
}
40+
41+
@action
42+
closeSubmitToBoaModal() {
43+
this.isSubmitToBoaModalOpen = false;
44+
}
45+
46+
@action
47+
openSubmitToBoaModal() {
48+
this.isSubmitToBoaModalOpen = true;
49+
}
50+
51+
get isBoaEnabled() {
52+
return this.args.addonsEnabled?.includes('boa');
53+
}
54+
55+
get showSubmitToBoa() {
56+
const { item, manager } = this.args;
57+
if (item.providerIsOsfstorage && item.isBoaFile && this.isBoaEnabled) {
58+
let userCanUploadToHere;
59+
if (manager) {
60+
userCanUploadToHere = manager.currentFolder.userCanUploadToHere;
61+
} else {
62+
const storage = (item.fileModel.target as unknown as NodeModel).get('storage');
63+
const writableTarget = item.currentUserPermission === 'write' && !item.targetIsRegistration;
64+
userCanUploadToHere = writableTarget && !storage.get('isOverStorageCap');
65+
}
66+
return userCanUploadToHere;
67+
}
68+
return false;
69+
}
3770
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { inject as service } from '@ember/service';
2+
import { waitFor } from '@ember/test-waiters';
3+
import Component from '@glimmer/component';
4+
import { task } from 'ember-concurrency';
5+
import IntlService from 'ember-intl/services/intl';
6+
import File from 'ember-osf-web/packages/files/file';
7+
import captureException, { getApiErrorMessage } from 'ember-osf-web/utils/capture-exception';
8+
import config from 'ember-osf-web/config/environment';
9+
import { tracked } from '@glimmer/tracking';
10+
import { action } from '@ember/object';
11+
12+
interface Args {
13+
file: File;
14+
isOpen: boolean;
15+
closeModal: () => {};
16+
}
17+
18+
export default class SubmitToBoaModal extends Component<Args> {
19+
@service toast!: Toastr;
20+
@service intl!: IntlService;
21+
@tracked selectedDataset?: string;
22+
23+
datasets = [
24+
'2022 Jan/Java',
25+
'2022 Feb/Python',
26+
'2021 Method Chains',
27+
'2021 Aug/Python',
28+
'2021 Aug/Kotlin (small)',
29+
'2021 Aug/Kotlin',
30+
'2021 Jan/ML-Verse',
31+
'2020 August/Python-DS',
32+
'2019 October/GitHub (small)',
33+
'2019 October/GitHub (medium)',
34+
'2019 October/GitHub',
35+
'2015 September/GitHub',
36+
'2013 September/SF (small)',
37+
'2013 September/SF (medium)',
38+
'2013 September/SF',
39+
'2013 May/SF',
40+
'2013 February/SF',
41+
'2012 July/SF',
42+
];
43+
44+
@action
45+
onDatasetChange(newDataset: string) {
46+
this.selectedDataset = newDataset;
47+
}
48+
49+
@task
50+
@waitFor
51+
async confirmSubmitToBoa() {
52+
try {
53+
const file = this.args.file;
54+
const fileModel = file.fileModel;
55+
const parentFolder = await fileModel.get('parentFolder');
56+
const grandparentFolder = await parentFolder.get('parentFolder');
57+
const endpoint = config.OSF.url + 'api/v1/project/' + fileModel.target.get('id') + '/boa/submit-job/';
58+
const uploadLink = new URL(parentFolder.get('links').upload as string);
59+
uploadLink.searchParams.set('kind', 'file');
60+
const payload = {
61+
data: {
62+
nodeId: fileModel.target.get('id'),
63+
name: file.name,
64+
materialized: fileModel.materializedPath,
65+
sizeInt: fileModel.size,
66+
links: {
67+
download: file.links.download,
68+
upload: file.links.upload,
69+
},
70+
},
71+
parent: {
72+
links: {
73+
upload: uploadLink.toString(),
74+
},
75+
isAddonRoot: !grandparentFolder,
76+
},
77+
dataset: this.selectedDataset,
78+
};
79+
await this.args.file.currentUser.authenticatedAJAX({
80+
url: endpoint,
81+
type: 'POST',
82+
data: JSON.stringify(payload),
83+
xhrFields: { withCredentials: true },
84+
headers: {
85+
'Content-Type': 'application/json',
86+
},
87+
});
88+
89+
this.args.closeModal();
90+
} catch (e) {
91+
captureException(e);
92+
const errorMessageKey = this.intl.t('osf-components.file-browser.submit_to_boa_fail',
93+
{ fileName: this.args.file.name, htmlSafe: true }) as string;
94+
this.toast.error(getApiErrorMessage(e), errorMessageKey);
95+
return;
96+
}
97+
98+
this.toast.success(
99+
this.intl.t('osf-components.file-browser.submit_to_boa_success', { fileName: this.args.file.name }),
100+
);
101+
}
102+
}

lib/osf-components/addon/components/file-actions-menu/submit-to-boa-modal/styles.scss

Whitespace-only changes.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<OsfDialog @isOpen={{@isOpen}} @onClose={{@closeModal}} as |dialog|>
2+
<dialog.heading>
3+
{{t 'osf-components.file-browser.submit_to_boa'}}
4+
</dialog.heading>
5+
<dialog.main>
6+
7+
<p>{{t 'osf-components.file-browser.boa_dataset_spiel'}}</p>
8+
<PowerSelect
9+
@options={{this.datasets}}
10+
@selected={{this.selectedDataset}}
11+
@placeholder={{t 'osf-components.file-browser.boa_dataset_select_placeholder'}}
12+
@onChange={{this.onDatasetChange}}
13+
as |dataset|
14+
>
15+
{{dataset}}
16+
</PowerSelect>
17+
<p>{{t 'osf-components.file-browser.confirm_submit_to_boa' fileName=@file.name}}</p>
18+
19+
</dialog.main>
20+
<dialog.footer>
21+
<Button
22+
{{on 'click' (fn (mut @isOpen) false)}}
23+
>
24+
{{t 'general.cancel'}}
25+
</Button>
26+
<Button
27+
@type='primary'
28+
disabled={{or this.confirmSubmitToBoa.isRunning (not this.selectedDataset)}}
29+
{{on 'click' (perform this.confirmSubmitToBoa)}}
30+
>
31+
{{t 'osf-components.file-browser.confirm_submit_to_boa_yes'}}
32+
</Button>
33+
</dialog.footer>
34+
</OsfDialog>

lib/osf-components/addon/components/file-actions-menu/template.hbs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,31 @@
110110
</Button>
111111
{{/if}}
112112
{{/if}}
113+
{{#if this.showSubmitToBoa}}
114+
<Button
115+
@layout='fake-link'
116+
data-test-submit-to-boa
117+
data-analytics-name='Submit to Boa'
118+
local-class='DropdownItem'
119+
{{on 'click' (queue
120+
dropdown.close
121+
this.openSubmitToBoaModal
122+
)}}
123+
>
124+
<FaIcon @icon='upload' />
125+
{{t 'file_actions_menu.submit_to_boa'}}
126+
</Button>
127+
{{/if}}
113128
</dropdown.content>
114129
</ResponsiveDropdown>
115130
<FileActionsMenu::DeleteModal @file={{@item}} @isOpen={{this.isDeleteModalOpen}} @closeModal={{this.closeDeleteModal}} @onDelete={{@onDelete}} />
131+
{{#if this.showSubmitToBoa}}
132+
<FileActionsMenu::SubmitToBoaModal
133+
@file={{@item}}
134+
@isOpen={{this.isSubmitToBoaModalOpen}}
135+
@closeModal={{this.closeSubmitToBoaModal}}
136+
/>
137+
{{/if}}
116138
{{#if @manager}}
117139
<MoveFileModal
118140
@isOpen={{this.moveModalOpen}}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,13 @@
8484
local-class='FileList__item__options'
8585
>
8686
{{#unless @manager.selectedFiles}}
87-
<FileActionsMenu @item={{@item}} @onDelete={{@manager.reload}} @manager={{@manager}} @allowRename={{true}} />
87+
<FileActionsMenu
88+
@item={{@item}}
89+
@onDelete={{@manager.reload}}
90+
@manager={{@manager}}
91+
@allowRename={{true}}
92+
@addonsEnabled={{@manager.targetNode.addonsEnabled}}
93+
/>
8894
{{/unless}}
8995
</div>
9096
</li>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from 'osf-components/components/file-actions-menu/submit-to-boa-modal/component';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from 'osf-components/components/file-actions-menu/submit-to-boa-modal/template';

0 commit comments

Comments
 (0)