diff --git a/config/.gitignore b/config/.gitignore index a420ca43025..28bcb46ad31 100644 --- a/config/.gitignore +++ b/config/.gitignore @@ -1,2 +1,3 @@ config.*.yml !config.example.yml +!config.tamu.yml diff --git a/config/config.tamu.yml b/config/config.tamu.yml new file mode 100644 index 00000000000..0be7102650c --- /dev/null +++ b/config/config.tamu.yml @@ -0,0 +1,47 @@ +rest: + ssl: true + host: api-dev.library.tamu.edu + port: 443 + nameSpace: /dspace7 + +mediaViewer: + image: true + video: false + +themes: + - name: capstone + extends: tamu + # handle: '1969.1/94971' + uuid: '330feb1a-a3b8-4ba1-91cd-adb9369b6fb3' + - name: esl + extends: tamu + # handle: '1969.1/1656' + uuid: '7243d893-6c7e-44fc-aa74-ecfbf7dc771c' + - name: periodicals + extends: tamu + # regex: '1969.1\/94990|1969.1\/6062' + regex: '0e87eb5c-ce4a-4e74-9d33-fcbfb5066209|4d390df8-2edf-4633-bfde-5b2df685c459' + - name: image-gallery + extends: tamu + # regex: '1969.1\/91004|1969.1\/590|1969.1\/97046|1969.1\/86434|1969.1\/128952|1969.1\/148733|1969.1\/155981|1969.1\/188336|1969.1\/155981|1969.1\/188335|1969.1\/194556|1969.1\/195994' + regex: 'befb7166-392b-445b-b907-1f4363bb6dfb|ed211f67-86ad-41ff-9696-fe0e7e17e1cb|d61f515b-8153-4718-9b9d-1ce679c955a0|c431e202-fc5b-46c8-b188-00901e1e215a|7c2ac72f-fd7a-4f27-b701-eb6e0539a876|beea4d64-0ff6-4e58-9f7b-489e1d6c6f75|282ec515-7b66-4492-b4d0-ae66d76f516b|6d433be8-7a28-4dc7-a245-f8dfbb7a513c|282ec515-7b66-4492-b4d0-ae66d76f516b|a3923d8b-249c-4b7d-8459-0c83953f7d8f|7c61a02c-2176-4c5a-9dd8-919353159e91|a4116d8b-8135-4449-a1b0-3378d5c7ecad' + - name: tamu + headTags: + - tagName: link + attributes: + rel: icon + href: assets/tamu/images/favicons/favicon.ico + sizes: any + - tagName: link + attributes: + rel: icon + href: assets/tamu/images/favicons/favicon.svg + type: image/svg+xml + - tagName: link + attributes: + rel: apple-touch-icon + href: assets/tamu/images/favicons/apple-touch-icon.png + - tagName: link + attributes: + rel: manifest + href: assets/tamu/images/favicons/manifest.webmanifest diff --git a/config/config.yml b/config/config.yml index 62c6ae401b7..507379b4910 100644 --- a/config/config.yml +++ b/config/config.yml @@ -2,46 +2,4 @@ rest: ssl: true host: demo.dspace.org port: 443 - nameSpace: /server - -mediaViewer: - image: true - video: false - -themes: - - name: capstone - extends: tamu - # handle: '1969.1/94971' - uuid: '330feb1a-a3b8-4ba1-91cd-adb9369b6fb3' - - name: esl - extends: tamu - # handle: '1969.1/1656' - uuid: '7243d893-6c7e-44fc-aa74-ecfbf7dc771c' - - name: periodicals - extends: tamu - # regex: '1969.1\/94990|1969.1\/6062' - regex: '0e87eb5c-ce4a-4e74-9d33-fcbfb5066209|4d390df8-2edf-4633-bfde-5b2df685c459' - - name: image-gallery - extends: tamu - # regex: '1969.1\/91004|1969.1\/590|1969.1\/97046|1969.1\/86434|1969.1\/128952|1969.1\/148733|1969.1\/155981|1969.1\/188336|1969.1\/155981|1969.1\/188335|1969.1\/194556|1969.1\/195994' - regex: 'befb7166-392b-445b-b907-1f4363bb6dfb|ed211f67-86ad-41ff-9696-fe0e7e17e1cb|d61f515b-8153-4718-9b9d-1ce679c955a0|c431e202-fc5b-46c8-b188-00901e1e215a|7c2ac72f-fd7a-4f27-b701-eb6e0539a876|beea4d64-0ff6-4e58-9f7b-489e1d6c6f75|282ec515-7b66-4492-b4d0-ae66d76f516b|6d433be8-7a28-4dc7-a245-f8dfbb7a513c|282ec515-7b66-4492-b4d0-ae66d76f516b|a3923d8b-249c-4b7d-8459-0c83953f7d8f|7c61a02c-2176-4c5a-9dd8-919353159e91|a4116d8b-8135-4449-a1b0-3378d5c7ecad' - - name: tamu - headTags: - - tagName: link - attributes: - rel: icon - href: assets/tamu/images/favicons/favicon.ico - sizes: any - - tagName: link - attributes: - rel: icon - href: assets/tamu/images/favicons/favicon.svg - type: image/svg+xml - - tagName: link - attributes: - rel: apple-touch-icon - href: assets/tamu/images/favicons/apple-touch-icon.png - - tagName: link - attributes: - rel: manifest - href: assets/tamu/images/favicons/manifest.webmanifest + nameSpace: /server \ No newline at end of file diff --git a/src/app/core/shared/collection.model.ts b/src/app/core/shared/collection.model.ts index c97c61eceb6..fd90db9c25c 100644 --- a/src/app/core/shared/collection.model.ts +++ b/src/app/core/shared/collection.model.ts @@ -33,6 +33,8 @@ export class Collection extends DSpaceObject implements ChildHALResource, Handle @deserialize _links: { license: HALLink; + // TAMU Customization - available licenses HALLink for this collection + licenses: HALLink; harvester: HALLink; mappedItems: HALLink; itemtemplate: HALLink; @@ -54,6 +56,13 @@ export class Collection extends DSpaceObject implements ChildHALResource, Handle @link(LICENSE) license?: Observable>; + /** + * TAMU Customization - The available licenses for this Collection + * Will be undefined unless the licenses {@link HALLink} has been resolved. + */ + @link(LICENSE) + licenses?: Observable>>; + /** * The logo for this Collection * Will be undefined unless the logo {@link HALLink} has been resolved. diff --git a/src/app/core/shared/license.model.ts b/src/app/core/shared/license.model.ts index 2b2477c1f8f..1cbd9102ef0 100644 --- a/src/app/core/shared/license.model.ts +++ b/src/app/core/shared/license.model.ts @@ -19,4 +19,11 @@ export class License extends DSpaceObject { */ @autoserialize text: string; + + /** + * TAMU Customization - The radio label for the license + */ + @autoserialize + label?: string; + } diff --git a/src/app/core/submission/models/workspaceitem-section-license.model.ts b/src/app/core/submission/models/workspaceitem-section-license.model.ts index 26f625871e7..2f42ca51b9b 100644 --- a/src/app/core/submission/models/workspaceitem-section-license.model.ts +++ b/src/app/core/submission/models/workspaceitem-section-license.model.ts @@ -1,3 +1,4 @@ +import { WorkspaceitemSectionUploadFileObject } from './workspaceitem-section-upload-file.model'; /** * An interface to represent submission's license section data. @@ -17,4 +18,14 @@ export interface WorkspaceitemSectionLicenseObject { * A boolean representing if license has been granted */ granted: boolean; + + /** + * TAMU Customization - A string representing which license has been selected + */ + selected?: string; + + /** + * TAMU Customization - A list of license files [[WorkspaceitemSectionUploadFileObject]] + */ + files?: WorkspaceitemSectionUploadFileObject[]; } diff --git a/src/app/shared/testing/theme-service.stub.ts b/src/app/shared/testing/theme-service.stub.ts new file mode 100644 index 00000000000..8c435d68409 --- /dev/null +++ b/src/app/shared/testing/theme-service.stub.ts @@ -0,0 +1,6 @@ +export class ThemeServiceStub { + + getThemeName = jasmine.createSpy('getThemeName'); + getThemeConfigFor = jasmine.createSpy('getThemeConfigFor'); + +} diff --git a/src/app/shared/upload/uploader/uploader-options.model.ts b/src/app/shared/upload/uploader/uploader-options.model.ts index 559fb0485b0..1735575dadd 100644 --- a/src/app/shared/upload/uploader/uploader-options.model.ts +++ b/src/app/shared/upload/uploader/uploader-options.model.ts @@ -22,6 +22,13 @@ export class UploaderOptions { */ maxFileNumber: number; + /** + * TAMU Customization - Additional parameters to inform which step made upload request + */ + additionalParameter?: { + [key: string]: any; + }; + /** * The request method to use for the file upload request */ diff --git a/src/app/shared/upload/uploader/uploader.component.ts b/src/app/shared/upload/uploader/uploader.component.ts index ef4ce4ee451..9cb4994c7c7 100644 --- a/src/app/shared/upload/uploader/uploader.component.ts +++ b/src/app/shared/upload/uploader/uploader.component.ts @@ -109,6 +109,8 @@ export class UploaderComponent { autoUpload: this.uploadFilesOptions.autoUpload, method: this.uploadFilesOptions.method, queueLimit: this.uploadFilesOptions.maxFileNumber, + // TAMU Customization - include additional parameter map to provide section id + additionalParameter: this.uploadFilesOptions.additionalParameter, }); if (isUndefined(this.enableDragOverDocument)) { diff --git a/src/app/submission/sections/container/section-container.component.spec.ts b/src/app/submission/sections/container/section-container.component.spec.ts index d3f4a93762a..7eb542a69e9 100644 --- a/src/app/submission/sections/container/section-container.component.spec.ts +++ b/src/app/submission/sections/container/section-container.component.spec.ts @@ -15,6 +15,8 @@ import { SubmissionService } from '../../submission.service'; import { SectionsService } from '../sections.service'; import { SubmissionServiceStub } from '../../../shared/testing/submission-service.stub'; import { SectionsServiceStub } from '../../../shared/testing/sections-service.stub'; +import { ThemeService } from '../../../shared/theme-support/theme.service'; +import { ThemeServiceStub } from '../../../shared/testing/theme-service.stub'; import { SectionDataObject } from '../models/section-data.model'; import { mockSubmissionCollectionId, mockSubmissionId } from '../../../shared/mocks/submission.mock'; @@ -51,6 +53,7 @@ describe('SubmissionSectionContainerComponent test suite', () => { const submissionServiceStub: SubmissionServiceStub = new SubmissionServiceStub(); const sectionsServiceStub: SectionsServiceStub = new SectionsServiceStub(); + const themeServiceStub: ThemeServiceStub = new ThemeServiceStub(); const submissionId = mockSubmissionId; const collectionId = mockSubmissionCollectionId; @@ -60,6 +63,8 @@ describe('SubmissionSectionContainerComponent test suite', () => { sectionsServiceStub.getSectionState.and.returnValue(observableOf(sectionState)); sectionsServiceStub.getShownSectionErrors.and.returnValue(observableOf([])); submissionServiceStub.getActiveSectionId.and.returnValue(observableOf('traditionalpageone')); + themeServiceStub.getThemeName.and.returnValue('dspace'); + themeServiceStub.getThemeConfigFor.and.returnValue({ name: 'dspace' }); } // waitForAsync beforeEach @@ -78,6 +83,7 @@ describe('SubmissionSectionContainerComponent test suite', () => { providers: [ { provide: SectionsService, useValue: sectionsServiceStub }, { provide: SubmissionService, useValue: submissionServiceStub }, + { provide: ThemeService, useValue: themeServiceStub }, SubmissionSectionContainerComponent ], schemas: [CUSTOM_ELEMENTS_SCHEMA] diff --git a/src/app/submission/sections/container/section-container.component.ts b/src/app/submission/sections/container/section-container.component.ts index 3331629f332..4ac0e100481 100644 --- a/src/app/submission/sections/container/section-container.component.ts +++ b/src/app/submission/sections/container/section-container.component.ts @@ -1,9 +1,10 @@ import { Component, Injector, Input, OnInit, ViewChild } from '@angular/core'; -import { SectionsDirective } from '../sections.directive'; +import { ThemeService } from 'src/app/shared/theme-support/theme.service'; +import { AlertType } from '../../../shared/alert/alert-type'; import { SectionDataObject } from '../models/section-data.model'; import { rendersSectionType } from '../sections-decorator'; -import { AlertType } from '../../../shared/alert/alert-type'; +import { SectionsDirective } from '../sections.directive'; /** * This component represents a section that contains the submission license form. @@ -50,12 +51,17 @@ export class SubmissionSectionContainerComponent implements OnInit { */ @ViewChild('sectionRef') sectionRef: SectionsDirective; + // TAMU Customization - theme service to get active theme + private themeService: ThemeService; + /** * Initialize instance variables * * @param {Injector} injector */ constructor(private injector: Injector) { + // TAMU Customization - inject theme service + this.themeService = injector.get(ThemeService); } /** @@ -88,6 +94,15 @@ export class SubmissionSectionContainerComponent implements OnInit { * Find the correct component based on the section's type */ getSectionContent(): string { - return rendersSectionType(this.sectionData.sectionType); + // TAMU Customization - provide injected active theme to get themed section to render + let theme = this.themeService.getThemeName(); + let themeConfig = this.themeService.getThemeConfigFor(theme); + // get theme "root class" + while (!!themeConfig.extends) { + themeConfig = this.themeService.getThemeConfigFor(theme); + } + + return rendersSectionType(this.sectionData.sectionType, themeConfig.name); + // return rendersSectionType(this.sectionData.sectionType); } } diff --git a/src/app/submission/sections/sections-decorator.ts b/src/app/submission/sections/sections-decorator.ts index 7e7840adfd0..d7a7ac7bdd5 100644 --- a/src/app/submission/sections/sections-decorator.ts +++ b/src/app/submission/sections/sections-decorator.ts @@ -2,15 +2,26 @@ import { SectionsType } from './sections-type'; const submissionSectionsMap = new Map(); -export function renderSectionFor(sectionType: SectionsType) { +// TAMU Customization - map sections by theme +export function renderSectionFor(sectionType: SectionsType, theme: string = 'dspace') { return function decorator(objectElement: any) { if (!objectElement) { return; } - submissionSectionsMap.set(sectionType, objectElement); + let themedMap = submissionSectionsMap.get(theme); + if (themedMap === undefined) { + themedMap = new Map(); + submissionSectionsMap.set(theme, themedMap); + } + themedMap.set(sectionType, objectElement); }; } -export function rendersSectionType(sectionType: SectionsType) { - return submissionSectionsMap.get(sectionType); +// TAMU Customization - get sections mapped by theme +export function rendersSectionType(sectionType: SectionsType, theme: string = 'dspace') { + let themedMap = submissionSectionsMap.get(theme); + if (themedMap === undefined || !themedMap.has(sectionType)) { + themedMap = submissionSectionsMap.get('dspace'); + } + return themedMap.get(sectionType); } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index e08750fc464..da049d98bb1 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4524,7 +4524,7 @@ "submission.sections.accesses.form.until-placeholder": "Until", - "submission.sections.license.granted-label": "I confirm the license above", + "submission.sections.license.granted-label": "I accept the terms of this license.", "submission.sections.license.required": "You must accept the license", @@ -5253,4 +5253,36 @@ "communityList.expandAll": "Expand All", "communityList.collapseAll": "Collapse All", + + "submission.sections.proxy-license.header": "Granting a License", + + "submission.sections.proxy-license.last-step-label": "There is one last step:", + + "submission.sections.proxy-license.last-step": "In order for OAKTrust to reproduce, translate and distribute your submission worldwide, you must agree to the following terms.", + + "submission.sections.proxy-license.instructions": "Please select the license that best matches your situation, grant the license by selecting 'I accept the terms of this license.'; and then click '+Deposit'.", + + "submission.sections.proxy-license.label": "Distribution license: ", + + "submission.sections.proxy-license.permission-upload-label": "Proxy Submission License: ", + + "submission.sections.proxy-license.permission-upload-instructions": "If you have a separate permission document from the copyright owner authorizing the release of this item, please attach it here.", + + "submission.sections.proxy-license.permission-upload-successful": "Proxy license permission upload successful", + + "submission.sections.proxy-license.permission-upload-failed": "Proxy license permission upload failed", + + "submission.sections.proxy-license.permission-upload-drop-message": "Drop permission document to attach to the item", + + "submission.sections.proxy-license.upload-label": "Proxy Submission License: ", + + "submission.sections.proxy-license.upload-drop-message": "Drop permission document to attach to the item", + + "submission.sections.proxy-license.upload-instructions": "If you have a separate permission document from the copyright owner authorizing the release of this item, please attach it here.", + + "submission.sections.proxy-license.upload-successful": "Proxy license upload successful", + + "submission.sections.proxy-license.upload-failed": "Proxy license upload failed", + + "submission.sections.proxy-license.remove-successful": "Proxy license remove successful", } diff --git a/src/themes/tamu/app/submission/sections/license/section-license.component.html b/src/themes/tamu/app/submission/sections/license/section-license.component.html new file mode 100644 index 00000000000..30527ca7c5c --- /dev/null +++ b/src/themes/tamu/app/submission/sections/license/section-license.component.html @@ -0,0 +1,83 @@ +
+
{{"submission.sections.proxy-license.header" | translate}}
+

+ {{"submission.sections.proxy-license.last-step-label" | translate}} + {{"submission.sections.proxy-license.last-step" | translate}} +

+

{{"submission.sections.proxy-license.instructions" | translate}}

+
+ +
+ +
+ +
+

+
+ +
+ + + +
+
+
+ {{proxy.metadata['dc.source'][0].value}} ({{proxy.sizeBytes | dsFileSize}}) +
+
+ + + + + + +
+
+
+
+ {{"submission.sections.proxy-license.permission-upload-instructions" | translate}} +
+ +
+ + +
+ + + + + + diff --git a/src/themes/tamu/app/submission/sections/license/section-license.component.ts b/src/themes/tamu/app/submission/sections/license/section-license.component.ts new file mode 100644 index 00000000000..a1d32e34cc8 --- /dev/null +++ b/src/themes/tamu/app/submission/sections/license/section-license.component.ts @@ -0,0 +1,325 @@ +import { ChangeDetectorRef, Component, ElementRef, Inject, ViewChild } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { DynamicCheckboxModel, DynamicFormLayout, DynamicRadioGroupModel, MATCH_DISABLED } from '@ng-dynamic-forms/core'; +import { TranslateService } from '@ngx-translate/core'; +import { BehaviorSubject, Observable, Subscription } from 'rxjs'; +import { distinctUntilChanged, filter, map, switchMap, take, tap } from 'rxjs/operators'; + +import { AuthService } from '../../../../../../app/core/auth/auth.service'; +import { BitstreamDataService } from '../../../../../../app/core/data/bitstream-data.service'; +import { CollectionDataService } from '../../../../../../app/core/data/collection-data.service'; +import { PaginatedList } from '../../../../../../app/core/data/paginated-list.model'; +import { RemoteData } from '../../../../../../app/core/data/remote-data'; +import { JsonPatchOperationsBuilder } from '../../../../../../app/core/json-patch/builder/json-patch-operations-builder'; +import { Collection } from '../../../../../../app/core/shared/collection.model'; +import { HALEndpointService } from '../../../../../../app/core/shared/hal-endpoint.service'; +import { License } from '../../../../../../app/core/shared/license.model'; +import { WorkspaceitemSectionLicenseObject } from '../../../../../../app/core/submission/models/workspaceitem-section-license.model'; +import { WorkspaceItem } from '../../../../../../app/core/submission/models/workspaceitem.model'; +import { SubmissionJsonPatchOperationsService } from '../../../../../../app/core/submission/submission-json-patch-operations.service'; +import { normalizeSectionData } from '../../../../../../app/core/submission/submission-response-parsing.service'; +import { isNotEmpty, isNotUndefined } from '../../../../../../app/shared/empty.util'; +import { FormBuilderService } from '../../../../../../app/shared/form/builder/form-builder.service'; +import { FormService } from '../../../../../../app/shared/form/form.service'; +import { NotificationsService } from '../../../../../../app/shared/notifications/notifications.service'; +import { UploaderOptions } from '../../../../../../app/shared/upload/uploader/uploader-options.model'; +import { followLink } from '../../../../../../app/shared/utils/follow-link-config.model'; +import { SectionFormOperationsService } from '../../../../../../app/submission/sections/form/section-form-operations.service'; +import { SubmissionSectionLicenseComponent as BaseComponent } from '../../../../../../app/submission/sections/license/section-license.component'; +import { SectionDataObject } from '../../../../../../app/submission/sections/models/section-data.model'; +import { renderSectionFor } from '../../../../../../app/submission/sections/sections-decorator'; +import { SectionsType } from '../../../../../../app/submission/sections/sections-type'; +import { SectionsService } from '../../../../../../app/submission/sections/sections.service'; +import { SectionUploadService } from '../../../../../../app/submission/sections/upload/section-upload.service'; +import { SubmissionService } from '../../../../../../app/submission/submission.service'; +import parseSectionErrors from '../../../../../../app/submission/utils/parseSectionErrors'; +import { SECTION_LICENSE_FORM_LAYOUT } from './section-license.model'; + +/** + * This component represents a section that contains the submission license form. + */ +@Component({ + selector: 'ds-submission-section-license', + // styleUrls: ['./section-license.component.scss'], + templateUrl: './section-license.component.html', + // templateUrl: '../../../../../../app/submission/sections/license/section-license.component.html', +}) +@renderSectionFor(SectionsType.License, 'tamu') +export class SubmissionSectionLicenseComponent extends BaseComponent { + + /** + * The [[DynamicFormLayout]] object + * @type {DynamicFormLayout} + */ + public formLayout: DynamicFormLayout = SECTION_LICENSE_FORM_LAYOUT; + + /** + * The uploader configuration options + * @type {UploaderOptions} + */ + public uploadFilesOptions: UploaderOptions = new UploaderOptions(); + + /** + * A boolean representing if is possible to active drop zone over the document page + * @type {boolean} + */ + public enableDragOverDocument = true; + + /** + * i18n message label + * @type {string} + */ + public dropMsg = 'submission.sections.proxy-license.upload-drop-message'; + + /** + * i18n message label + * @type {string} + */ + public dropOverDocumentMsg = 'submission.sections.proxy-license.upload-drop-message'; + + public proxyLicense: Observable; + + public license: BehaviorSubject; + + public selected: BehaviorSubject; + + public removingProxy: BehaviorSubject; + + /** + * The license label wrapper element reference + */ + @ViewChild('labelWrapper') private labelWrapper: ElementRef; + + /** + * The license text wrapper element reference + */ + @ViewChild('licenseWrapper') private licenseWrapper: ElementRef; + + /** + * The proxy permission license uploader wrapper element reference + */ + @ViewChild('uploaderWrapper') private uploaderWrapper: ElementRef; + + /** + * The license step form wrapper element reference + */ + @ViewChild('formWrapper') private formWrapper: ElementRef; + + constructor( + private authService: AuthService, + private bitstreamService: BitstreamDataService, + private halEndpointService: HALEndpointService, + private modalService: NgbModal, + private notificationsService: NotificationsService, + private operationsService: SubmissionJsonPatchOperationsService, + private sectionUploadService: SectionUploadService, + protected changeDetectorRef: ChangeDetectorRef, + protected collectionDataService: CollectionDataService, + protected formBuilderService: FormBuilderService, + protected formOperationsService: SectionFormOperationsService, + protected formService: FormService, + protected operationsBuilder: JsonPatchOperationsBuilder, + protected sectionService: SectionsService, + protected submissionService: SubmissionService, + protected translateService: TranslateService, + @Inject('collectionIdProvider') public injectedCollectionId: string, + @Inject('sectionDataProvider') public injectedSectionData: SectionDataObject, + @Inject('submissionIdProvider') public injectedSubmissionId: string + ) { + super( + changeDetectorRef, + collectionDataService, + formBuilderService, + formOperationsService, + formService, + operationsBuilder, + sectionService, + submissionService, + translateService, + injectedCollectionId, + injectedSectionData, + injectedSubmissionId + ); + this.license = new BehaviorSubject(undefined); + this.selected = new BehaviorSubject(undefined); + this.removingProxy = new BehaviorSubject(false); + } + + ngOnInit(): void { + this.subs.push( + this.halEndpointService.getEndpoint(this.submissionService.getSubmissionObjectLinkName()).pipe( + filter((href: string) => isNotEmpty(href)), + distinctUntilChanged()) + .subscribe((endpointURL) => { + this.uploadFilesOptions.additionalParameter = { + sectionId: this.sectionData.id + }; + this.uploadFilesOptions.authToken = this.authService.buildAuthHeader(); + this.uploadFilesOptions.url = endpointURL.concat(`/${this.submissionId}`); + this.changeDetectorRef.detectChanges(); + }) + ); + + this.proxyLicense = this.sectionUploadService.getUploadedFileList(this.submissionId, this.sectionData.id) + .pipe( + tap(() => this.removingProxy.next(false)), + filter((files) => !!files && files.length), + map((files) => files.find((file) => file.metadata['dc.description'] + && file.metadata['dc.description'].length > 0 + && file.metadata['dc.description'][0].value === 'Proxy license'))); + + // get the available licenses by following collection licenses link + this.collectionDataService.findById(this.collectionId, true, true, followLink('licenses')).pipe( + filter((collectionData: RemoteData) => isNotUndefined((collectionData.payload))), + take(1), + switchMap((collectionData: RemoteData) => (collectionData.payload as any).licenses), + filter((licenseData: RemoteData>) => licenseData.hasSucceeded), + map((licenseData: RemoteData>) => licenseData.payload.page), + filter((licences: License[]) => !!licences && Array.isArray(licences) && licences.length > 0), + take(1) + ).subscribe((licences: License[]) => { + + // initialize the base component + this.onSectionInit(); + + // create a license form model with license options + const licenseFormModel = this.formBuilderService.fromJSON([ + { + id: 'selected', + options: licences.map((license: License) => { + return { + label: license.label, + value: license.name + }; + }), + type: 'RADIO_GROUP', + } + ]); + + const getLicense = (name: string) => licences.find((license: License) => license.name === name); + + // get the license granted form control model + const grantedFormControlModel = this.formBuilderService.findById('granted', this.formModel); + + // get the license selected form control model + const selectionFormControlModel = this.formBuilderService.findById('selected', licenseFormModel); + + this.subs.push( + // listen license selected/change and show license + (selectionFormControlModel as DynamicRadioGroupModel).valueChanges.pipe( + filter((name: string) => !!name) + ).subscribe((name: string) => { + + if (name !== (this.sectionData.data as WorkspaceitemSectionLicenseObject).selected) { + (grantedFormControlModel as DynamicCheckboxModel).value = false; + } + + setTimeout(() => { + let children = this.formWrapper.nativeElement.children; + while (!!children && children.length > 0 && !children[0].classList.contains('tamu-control')) { + children = children[0].children; + } + let control = children[children.length - 1]; + while (control.classList.contains('tamu-control-license')) { + control.remove(); + control = children[children.length - 1]; + } + + control.parentNode.insertBefore(this.labelWrapper.nativeElement, control.nextSibling); + + if (name === 'proxy') { + control.parentNode.insertBefore(this.uploaderWrapper.nativeElement, control.nextSibling); + } + + control.parentNode.insertBefore(this.licenseWrapper.nativeElement, control.nextSibling); + + const license = getLicense(name); + this.license.next(license.text.replace(/\n/g, '
')); + + this.selected.next(name); + }); + }) + ); + + (selectionFormControlModel as DynamicRadioGroupModel).value = (this.sectionData.data as WorkspaceitemSectionLicenseObject).selected; + + grantedFormControlModel.relations.push({ + match: MATCH_DISABLED, + when: [{ id: 'selected', value: null }], + }); + + // add the license form control model to the base component form model + this.formModel.unshift(selectionFormControlModel); + + this.updateSectionStatus(); + }); + } + + /** + * Open confirm modal and remove when confirmed. + * + * @param content element reference for modal + * @param proxy license bitstream + */ + public confirmRemoveProxy(content, proxy): void { + this.modalService.open(content).result.then( + (result) => { + if (result === 'ok') { + this.removingProxy.next(true); + this.bitstreamService.delete(proxy.uuid).pipe(take(1)).subscribe(() => { + this.removingProxy.next(false); + this.sectionUploadService.removeUploadedFile(this.submissionId, this.sectionData.id, proxy.uuid); + this.notificationsService.success(null, this.translateService.get('submission.sections.proxy-license.remove-successful')); + }); + } + } + ); + } + + /** + * Nothing to do here + * + * @returns empty observable + */ + public onBeforeUpload = () => { + const sub: Subscription = this.operationsService.jsonPatchByResourceType( + this.submissionService.getSubmissionObjectLinkName(), + this.submissionId, + 'sections') + .subscribe(); + this.subs.push(sub); + return sub; + }; + + /** + * Show success notification on upload successful and update section data + */ + public onCompleteItem(workspaceitem: WorkspaceItem) { + const { sections } = workspaceitem; + const { errors } = workspaceitem; + const errorsList = parseSectionErrors(errors); + if (sections && isNotEmpty(sections)) { + Object.keys(sections) + .forEach((sectionId) => { + const sectionData = normalizeSectionData(sections[sectionId]); + const sectionErrors = errorsList[sectionId]; + this.sectionService.isSectionType(this.submissionId, sectionId, SectionsType.License) + .pipe(take(1)) + .subscribe((isLicence) => { + if (isLicence) { + this.notificationsService.success(null, this.translateService.get('submission.sections.proxy-license.upload-successful')); + } + }); + this.sectionService.updateSectionData(this.submissionId, sectionId, sectionData, sectionErrors, sectionErrors); + }); + } + } + + /** + * Show error notification on upload failed + */ + public onUploadError() { + this.notificationsService.error(null, this.translateService.get('submission.sections.proxy-license.upload-failed')); + } + +} diff --git a/src/themes/tamu/app/submission/sections/license/section-license.model.ts b/src/themes/tamu/app/submission/sections/license/section-license.model.ts new file mode 100644 index 00000000000..662f55089d1 --- /dev/null +++ b/src/themes/tamu/app/submission/sections/license/section-license.model.ts @@ -0,0 +1,16 @@ +export const SECTION_LICENSE_FORM_LAYOUT = { + selected: { + element: { + container: 'tamu-control tamu-radio pl-1', + control: 'tamu-control-input', + label: 'tamu-control-label pt-1' + } + }, + granted: { + element: { + container: 'custom-control custom-checkbox pl-1', + control: 'custom-control-input', + label: 'custom-control-label pt-1' + } + } +}; diff --git a/src/themes/tamu/assets/i18n/en.json5 b/src/themes/tamu/assets/i18n/en.json5 index 11e2a9f6d6f..49c4f7fcbf6 100644 --- a/src/themes/tamu/assets/i18n/en.json5 +++ b/src/themes/tamu/assets/i18n/en.json5 @@ -19,4 +19,28 @@ "communityList.collapseAll": "Collapse All", + "submission.sections.license.granted-label": "I accept the terms of this license.", + + "submission.sections.proxy-license.label": "Distribution license: ", + + "submission.sections.proxy-license.header": "Granting a License", + + "submission.sections.proxy-license.last-step-label": "There is one last step:", + + "submission.sections.proxy-license.last-step": "In order for OAKTrust to reproduce, translate and distribute your submission worldwide, you must agree to the following terms.", + + "submission.sections.proxy-license.instructions": "Please select the license that best matches your situation, grant the license by selecting 'I accept the terms of this license.'; and then click '+Deposit'.", + + "submission.sections.proxy-license.upload-label": "Proxy Submission License: ", + + "submission.sections.proxy-license.upload-drop-message": "Drop permission document to attach to the item", + + "submission.sections.proxy-license.upload-instructions": "If you have a separate permission document from the copyright owner authorizing the release of this item, please attach it here.", + + "submission.sections.proxy-license.upload-successful": "Proxy license upload successful", + + "submission.sections.proxy-license.upload-failed": "Proxy license upload failed", + + "submission.sections.proxy-license.remove-successful": "Proxy license remove successful", + } diff --git a/src/themes/tamu/eager-theme.module.ts b/src/themes/tamu/eager-theme.module.ts index fea4fecd485..459ad0199f9 100644 --- a/src/themes/tamu/eager-theme.module.ts +++ b/src/themes/tamu/eager-theme.module.ts @@ -8,6 +8,7 @@ import { RootModule } from '../../app/root.module'; import { SharedBrowseByModule } from '../../app/shared/browse-by/shared-browse-by.module'; import { ComcolModule } from '../../app/shared/comcol/comcol.module'; import { DsoPageModule } from '../../app/shared/dso-page/dso-page.module'; +import { FormModule } from '../../app/shared/form/form.module'; import { ResultsBackButtonModule } from '../../app/shared/results-back-button/results-back-button.module'; import { SharedModule } from '../../app/shared/shared.module'; import { StatisticsModule } from '../../app/statistics/statistics.module'; @@ -17,13 +18,15 @@ import { HeaderNavbarWrapperComponent } from './app/header-nav-wrapper/header-na import { HeaderComponent } from './app/header/header.component'; import { HomeNewsComponent } from './app/home-page/home-news/home-news.component'; import { NavbarComponent } from './app/navbar/navbar.component'; +import { SubmissionSectionLicenseComponent } from './app/submission/sections/license/section-license.component'; /** * Add components that use a custom decorator to ENTRY_COMPONENTS as well as DECLARATIONS. * This will ensure that decorator gets picked up when the app loads */ const ENTRY_COMPONENTS = [ - CommunityPageComponent + CommunityPageComponent, + SubmissionSectionLicenseComponent ]; const DECLARATIONS = [ @@ -38,6 +41,7 @@ const DECLARATIONS = [ @NgModule({ imports: [ CommonModule, + FormModule, SharedModule, SharedBrowseByModule, ResultsBackButtonModule, @@ -46,6 +50,7 @@ const DECLARATIONS = [ ComcolModule, DsoPageModule, StatisticsModule, + // SubmissionModule, CommunityPageModule, CdkTreeModule, ], diff --git a/src/themes/tamu/styles/_global-styles.scss b/src/themes/tamu/styles/_global-styles.scss index 5bd4c19bc04..6757c6956e5 100644 --- a/src/themes/tamu/styles/_global-styles.scss +++ b/src/themes/tamu/styles/_global-styles.scss @@ -3,7 +3,8 @@ // imports the base global style @import '../../../styles/_global-styles.scss'; -.facet-filter, .setting-option { +.facet-filter, +.setting-option { background-color: var(--bs-light); border-radius: var(--bs-border-radius); @@ -21,3 +22,32 @@ font-size: 1.1rem } } + +.tamu-control-input { + display: flex; + flex-direction: column; + + label { + text-align: left; + padding-left: 0px; + box-shadow: none !important; + + input[type=radio] { + position: relative !important; + } + + span { + margin-left: 10px !important; + } + } +} + +.tamu-control-label { + font-weight: bold; + margin: 15px 0px 0px 0px; +} + + +.tamu-control-license { + margin: 0px 0px 10px 0px; +}