From b775cd5ab056a30f71099d274d3ac1b3254a30d4 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Mon, 16 Aug 2021 18:42:58 +0200 Subject: [PATCH 01/29] [CST-7757] Subscriptions porting (wip) --- src/app/app-routing-paths.ts | 6 + src/app/app-routing.module.ts | 6 + src/app/core/core.module.ts | 4 +- .../data/feature-authorization/feature-id.ts | 3 +- .../publication/publication.component.html | 1 + .../user-menu/user-menu.component.html | 2 + .../user-menu/user-menu.component.ts | 7 +- ...so-page-subscription-button.component.html | 6 + ...so-page-subscription-button.component.scss | 0 ...page-subscription-button.component.spec.ts | 25 + .../dso-page-subscription-button.component.ts | 57 + src/app/shared/shared.module.ts | 5 +- .../subscription-edit-modal.component.html | 44 + .../subscription-edit-modal.component.scss | 4 + .../subscription-edit-modal.component.spec.ts | 116 + .../subscription-edit-modal.component.ts | 190 + .../subscription-modal.component.html | 46 + .../subscription-modal.component.scss | 12 + .../subscription-modal.component.spec.ts | 223 + .../subscription-modal.component.ts | 306 ++ .../subscription-view.component.html | 29 + .../subscription-view.component.scss | 0 .../subscription-view.component.spec.ts | 120 + .../subscription-view.component.ts | 102 + .../models/subscription.model.ts | 57 + .../models/subscription.resource-type.ts | 10 + .../subscriptions/subscription.service.ts | 166 + .../subscriptions/subscriptions.module.ts | 31 + .../testing/notifications-service.stub.ts | 3 + src/app/shared/testing/router.stub.ts | 6 + .../shared/testing/subscriptions-data.mock.ts | 4467 +++++++++++++++++ src/app/shared/testing/test-module.ts | 7 +- .../subscriptions-page-routing.module.ts | 27 + .../subscriptions-page.component.html | 45 + .../subscriptions-page.component.scss | 0 .../subscriptions-page.component.spec.ts | 127 + .../subscriptions-page.component.ts | 117 + .../subscriptions-page.module.ts | 15 + src/assets/i18n/en.json5 | 16 + 39 files changed, 6400 insertions(+), 8 deletions(-) create mode 100644 src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html create mode 100644 src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.scss create mode 100644 src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.spec.ts create mode 100644 src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.ts create mode 100644 src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.html create mode 100644 src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.scss create mode 100644 src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.spec.ts create mode 100644 src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.ts create mode 100644 src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.html create mode 100644 src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.scss create mode 100644 src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.spec.ts create mode 100644 src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts create mode 100644 src/app/shared/subscriptions/components/subscription-view/subscription-view.component.html create mode 100644 src/app/shared/subscriptions/components/subscription-view/subscription-view.component.scss create mode 100644 src/app/shared/subscriptions/components/subscription-view/subscription-view.component.spec.ts create mode 100644 src/app/shared/subscriptions/components/subscription-view/subscription-view.component.ts create mode 100644 src/app/shared/subscriptions/models/subscription.model.ts create mode 100644 src/app/shared/subscriptions/models/subscription.resource-type.ts create mode 100644 src/app/shared/subscriptions/subscription.service.ts create mode 100644 src/app/shared/subscriptions/subscriptions.module.ts create mode 100644 src/app/shared/testing/subscriptions-data.mock.ts create mode 100644 src/app/subscriptions-page/subscriptions-page-routing.module.ts create mode 100644 src/app/subscriptions-page/subscriptions-page.component.html create mode 100644 src/app/subscriptions-page/subscriptions-page.component.scss create mode 100644 src/app/subscriptions-page/subscriptions-page.component.spec.ts create mode 100644 src/app/subscriptions-page/subscriptions-page.component.ts create mode 100644 src/app/subscriptions-page/subscriptions-page.module.ts diff --git a/src/app/app-routing-paths.ts b/src/app/app-routing-paths.ts index e9a6376884..fe2837c6e3 100644 --- a/src/app/app-routing-paths.ts +++ b/src/app/app-routing-paths.ts @@ -126,3 +126,9 @@ export function getRequestCopyModulePath() { } export const HEALTH_PAGE_PATH = 'health'; + +export const SUBSCRIPTIONS_MODULE_PATH = 'subscriptions'; + +export function getSubscriptionsModuleRoute() { + return `/${SUBSCRIPTIONS_MODULE_PATH}`; +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index d426b041ce..9779c2ab27 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -230,6 +230,12 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone loadChildren: () => import('./access-control/access-control.module').then((m) => m.AccessControlModule), canActivate: [GroupAdministratorGuard], }, + { + path: 'subscriptions', + loadChildren: () => import('./subscriptions-page/subscriptions-page-routing.module') + .then((m) => m.SubscriptionsPageRoutingModule), + canActivate: [AuthenticatedGuard] + }, { path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent }, ] } diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index ede23ba43b..6ac92d95f2 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -170,6 +170,7 @@ import { OrcidHistory } from './orcid/model/orcid-history.model'; import { OrcidAuthService } from './orcid/orcid-auth.service'; import { VocabularyDataService } from './submission/vocabularies/vocabulary.data.service'; import { VocabularyEntryDetailsDataService } from './submission/vocabularies/vocabulary-entry-details.data.service'; +import { Subscription } from '../shared/subscriptions/models/subscription.model'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -356,7 +357,8 @@ export const models = ResearcherProfile, OrcidQueue, OrcidHistory, - AccessStatusObject + AccessStatusObject, + Subscription ]; @NgModule({ diff --git a/src/app/core/data/feature-authorization/feature-id.ts b/src/app/core/data/feature-authorization/feature-id.ts index 3cb18bf515..d381a8214d 100644 --- a/src/app/core/data/feature-authorization/feature-id.ts +++ b/src/app/core/data/feature-authorization/feature-id.ts @@ -29,5 +29,6 @@ export enum FeatureID { CanViewUsageStatistics = 'canViewUsageStatistics', CanSendFeedback = 'canSendFeedback', CanClaimItem = 'canClaimItem', - CanSynchronizeWithORCID = 'canSynchronizeWithORCID' + CanSynchronizeWithORCID = 'canSynchronizeWithORCID', + CanSubscribe = 'canSubscribeDso', } diff --git a/src/app/item-page/simple/item-types/publication/publication.component.html b/src/app/item-page/simple/item-types/publication/publication.component.html index 181480b789..d248ee3416 100644 --- a/src/app/item-page/simple/item-types/publication/publication.component.html +++ b/src/app/item-page/simple/item-types/publication/publication.component.html @@ -15,6 +15,7 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> +
diff --git a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html index e730b0d85c..5643f3b9a8 100644 --- a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html +++ b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html @@ -6,6 +6,8 @@ {{'nav.profile' | translate}} {{'nav.mydspace' | translate}} + {{'nav.subscriptions' | translate}} +
diff --git a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts index 22b076c31a..114c711a9b 100644 --- a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts +++ b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts @@ -8,7 +8,7 @@ import { AppState } from '../../../app.reducer'; import { isAuthenticationLoading } from '../../../core/auth/selectors'; import { MYDSPACE_ROUTE } from '../../../my-dspace-page/my-dspace-page.component'; import { AuthService } from '../../../core/auth/auth.service'; -import { getProfileModuleRoute } from '../../../app-routing-paths'; +import { getProfileModuleRoute, getSubscriptionsModuleRoute } from '../../../app-routing-paths'; /** * This component represents the user nav menu. @@ -48,6 +48,11 @@ export class UserMenuComponent implements OnInit { */ public profileRoute = getProfileModuleRoute(); + /** + * The profile page route + */ + public subscriptionsRoute = getSubscriptionsModuleRoute(); + constructor(private store: Store, private authService: AuthService) { } diff --git a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html new file mode 100644 index 0000000000..9956163944 --- /dev/null +++ b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html @@ -0,0 +1,6 @@ + diff --git a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.scss b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.spec.ts b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.spec.ts new file mode 100644 index 0000000000..20a99cb926 --- /dev/null +++ b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DsoPageSubscriptionButtonComponent } from './dso-page-subscription-button.component'; + +describe('DsoPageSubscriptionButtonComponent', () => { + let component: DsoPageSubscriptionButtonComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DsoPageSubscriptionButtonComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DsoPageSubscriptionButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.ts b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.ts new file mode 100644 index 0000000000..526a99355f --- /dev/null +++ b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.ts @@ -0,0 +1,57 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; +import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { AuthService } from '../../../core/auth/auth.service'; +import { EPerson } from '../../../core/eperson/models/eperson.model'; +import { take } from 'rxjs/operators'; +import { + SubscriptionModalComponent +} from '../../subscriptions/components/subscription-modal/subscription-modal.component'; +import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; + +@Component({ + selector: 'ds-dso-page-subscription-button', + templateUrl: './dso-page-subscription-button.component.html', + styleUrls: ['./dso-page-subscription-button.component.scss'] +}) +export class DsoPageSubscriptionButtonComponent implements OnInit { + + /** + * Whether the current user is authorized to edit the DSpaceObject + */ + isAuthorized$: Observable = of(false); + + /** + * Reference to NgbModal + */ + public modalRef: NgbModalRef; + + /** + * EPerson id of the logged user + */ + ePersonId: string; + + /** + * DSpaceObject that is being viewed + */ + @Input() + dso: DSpaceObject; + + constructor( + protected authorizationService: AuthorizationDataService, + private modalService: NgbModal, + ) { + } + + ngOnInit(): void { + this.isAuthorized$ = this.authorizationService.isAuthorized(FeatureID.CanSubscribe, this.dso.self); + } + + public openSubscriptionModal() { + this.modalRef = this.modalService.open(SubscriptionModalComponent); + this.modalRef.componentInstance.dso = this.dso; + } + +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 13fdd3e12c..797587750b 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -315,6 +315,7 @@ import { MenuModule } from './menu/menu.module'; import { ListableNotificationObjectComponent } from './object-list/listable-notification-object/listable-notification-object.component'; +import { DsoPageSubscriptionButtonComponent } from './dso-page/dso-page-subscription-button/dso-page-subscription-button.component'; const MODULES = [ CommonModule, @@ -510,8 +511,8 @@ const SHARED_ITEM_PAGE_COMPONENTS = [ GenericItemPageFieldComponent, MetadataRepresentationListComponent, RelatedItemsComponent, - DsoPageOrcidButtonComponent - + DsoPageOrcidButtonComponent, + DsoPageSubscriptionButtonComponent, ]; const PROVIDERS = [ diff --git a/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.html b/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.html new file mode 100644 index 0000000000..7614005dff --- /dev/null +++ b/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.html @@ -0,0 +1,44 @@ + + + diff --git a/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.scss b/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.scss new file mode 100644 index 0000000000..fb9db5b0ff --- /dev/null +++ b/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.scss @@ -0,0 +1,4 @@ +.alert{ + font-weight: bold; + color:red; +} diff --git a/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.spec.ts b/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.spec.ts new file mode 100644 index 0000000000..d406fa2c1a --- /dev/null +++ b/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.spec.ts @@ -0,0 +1,116 @@ +import { ComponentFixture, ComponentFixtureAutoDetect, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing'; +import { cold } from 'jasmine-marbles'; + +// Import modules +import { CommonModule } from '@angular/common'; +import { ReactiveFormsModule } from '@angular/forms'; +import { By } from '@angular/platform-browser'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { DebugElement } from '@angular/core'; + +import { SubscriptionEditModalComponent } from './subscription-edit-modal.component'; + +// Import mocks +import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock'; +import { subscription } from '../../../testing/subscriptions-data.mock'; +import { ItemInfo } from '../../../testing/relationships-mocks'; + +// Import utils +import { NotificationsService } from '../../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; +import { SubscriptionService } from '../../subscription.service'; +import { Subscription } from '../../models/subscription.model'; + + +describe('SubscriptionEditModalComponent', () => { + let component: SubscriptionEditModalComponent; + let fixture: ComponentFixture; + let de: DebugElement; + + const subscriptionServiceStub = jasmine.createSpyObj('SubscriptionService', { + updateSubscription: jasmine.createSpy('updateSubscription'), + }); + + + beforeEach(waitForAsync (() => { + TestBed.configureTestingModule({ + imports: [ + CommonModule, + ReactiveFormsModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + declarations: [ SubscriptionEditModalComponent ], + providers: [ + { provide: ComponentFixtureAutoDetect, useValue: true }, + { provide: NotificationsService, useValue: NotificationsServiceStub }, + { provide: SubscriptionService, useValue: subscriptionServiceStub }, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SubscriptionEditModalComponent); + component = fixture.componentInstance; + component.eperson = 'testid123'; + component.dso = ItemInfo.payload; + + de = fixture.debugElement; + + subscriptionServiceStub.updateSubscription.and.returnValue(cold('a|', { + a: {} + })); + + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('No Subscription inserted', () => { + it('should not show form', () => { + expect(de.query(By.css('form'))).toBeNull(); + }); + }); + + + describe('Subscription inserted', () => { + + beforeEach(fakeAsync(() => { + component.subscription = Object.assign(new Subscription(), subscription); + component.ngOnInit(); + fixture.detectChanges(); + })); + + it('when insert subscription show form', () => { + expect(de.query(By.css('form'))).toBeTruthy(); + }); + + it('should have right checkboxes checked', () => { + expect(de.query(By.css('#checkbox-0'))?.nativeElement?.checked).toEqual(true); + expect(de.query(By.css('#checkbox-1'))?.nativeElement?.checked).toEqual(true); + expect(de.query(By.css('#checkbox-2'))?.nativeElement?.checked).toEqual(false); + }); + + it('on checkbox clicked should change form values', () => { + const checkbox = de.query(By.css('#checkbox-2')).nativeElement; + checkbox.click(); + + expect(de.query(By.css('#checkbox-2'))?.nativeElement?.checked).toEqual(true); + expect(component.subscriptionParameterList?.value?.length).toEqual(3); + }); + + it('on submit clicked update should have been called', () => { + const button = de.query(By.css('.btn-success')).nativeElement; + button.click(); + expect(subscriptionServiceStub.updateSubscription).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.ts b/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.ts new file mode 100644 index 0000000000..46c895dc18 --- /dev/null +++ b/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.ts @@ -0,0 +1,190 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; + +import { Subscription } from '../../models/subscription.model'; + +import { BehaviorSubject } from 'rxjs'; + +import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; + +import { SubscriptionService } from '../../subscription.service'; +import { NotificationsService } from '../../../notifications/notifications.service'; + +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + selector: 'ds-subscription-edit-modal', + templateUrl: './subscription-edit-modal.component.html', + styleUrls: ['./subscription-edit-modal.component.scss'] +}) +export class SubscriptionEditModalComponent implements OnInit { + + + /** + * DSpaceObject of the subscription + */ + @Input() dso: DSpaceObject; + + /** + * EPerson of the subscription + */ + @Input() eperson: string; + + /** + * List of subscription for the dso object and eperson relation + */ + @Input() subscription!: Subscription; + + /** + * Close event emit to close modal + */ + @Output() close: EventEmitter = new EventEmitter(); + + /** + * Reload event emit to refresh informations + */ + @Output() reload: EventEmitter = new EventEmitter(); + + /** + * A boolean representing if a request operation is pending + * @type {BehaviorSubject} + */ + public processing$ = new BehaviorSubject(false); + + /** + * Reactive form group that will be used to add subscriptions + */ + subscriptionForm: FormGroup; + + /** + * Used to show validation errors when user submits + */ + submitted = false; + + /** + * Reference to NgbModal + */ + public modalRef: NgbModalRef; + + + /** + * Frequencies to be shown as checkboxes + */ + frequencies = [ + {name: 'daily' ,value: 'D'}, + {name: 'monthly' ,value: 'M'}, + {name: 'weekly' ,value: 'W'}, + ]; + + constructor(private formGroup: FormBuilder, + private notificationsService: NotificationsService, + private subscriptionService: SubscriptionService + ) {} + + /** + * When component starts initialize starting functionality + */ + ngOnInit(): void { + this.initSubscription(); + } + + /** + * If the subscription is passed start the form with the information of subscription + */ + initSubscription(): void { + if (!!this.subscription) { + this.buildFormBuilder(this.subscription); + } + } + + /** + * Function to get subscriptionParameterList form array cleaner + */ + get subscriptionParameterList(): FormArray { + return this.subscriptionForm.get('subscriptionParameterList') as FormArray; + } + + /** + * When frequency checkboxes are being changed we add/remove frequencies from subscriptionParameterList + */ + selectCheckbox(event,frequency): void { + if (event.target.checked) { + this.addFrequency(frequency); + } else { + this.removeFrequency(frequency); + } + } + + /** + * Start the form with preinserted informations + */ + buildFormBuilder(subscription): void { + + this.subscriptionForm = this.formGroup.group({ + id: subscription.id, + type: subscription.subscriptionType, + subscriptionParameterList: this.formGroup.array([], Validators.required) + }); + + subscription.subscriptionParameterList.forEach( (parameter) => { + this.addFrequency(parameter.value); + }); + } + + /** + * Add a new frequency to the subscriptionParameterList form array + */ + addFrequency(frequency): void { + this.subscriptionParameterList.push( + this.formGroup.group({ + name: 'frequency', + value: frequency + }) + ); + } + + /** + * Remove frequency from subscriptionParameterList form array + */ + removeFrequency(frequency): void { + const index = this.subscriptionParameterList.controls.findIndex(el => el.value.value === frequency); + this.subscriptionParameterList.removeAt(index); + } + + /** + * When user saves it will check if form is valid and send request to update subscription + */ + submit(): void { + this.submitted = true; + if (this.subscriptionForm.valid) { + if (this.subscriptionForm.value.id) { + this.updateForm(this.subscriptionForm.value); + } + } + } + + /** + * Sends request to update a new subscription, refreshes the table of subscriptions and notifies about summary page + */ + updateForm(body): void { + this.subscriptionService.updateSubscription(body,this.eperson,this.dso.uuid).subscribe( (res) => { + this.reload.emit(); + this.close.emit(); + }); + } + + /** + * When close button is pressed emit function to close modal + */ + c(text): void { + this.close.emit(text); + } + + /** + * Returns if a specific frequency exists in the subscriptionParameterList + */ + getIsChecked(frequency): boolean { + return !!this.subscriptionForm.get('subscriptionParameterList').value.find(el => el.value === frequency.value); + } +} diff --git a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.html b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.html new file mode 100644 index 0000000000..53119d9a15 --- /dev/null +++ b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.html @@ -0,0 +1,46 @@ +
+ + + + +
diff --git a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.scss b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.scss new file mode 100644 index 0000000000..62dd1105e7 --- /dev/null +++ b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.scss @@ -0,0 +1,12 @@ +.alert{ + font-weight: bold; + color:red; +} + +// .modal-footer{ +// justify-content: space-between; +// } + +.add-button{ + padding: 0px 15px 15px 15px; +} \ No newline at end of file diff --git a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.spec.ts b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.spec.ts new file mode 100644 index 0000000000..088ec3004c --- /dev/null +++ b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.spec.ts @@ -0,0 +1,223 @@ +import { ComponentFixture, ComponentFixtureAutoDetect, TestBed } from '@angular/core/testing'; + +import { of as observableOf } from 'rxjs'; + +// Import modules +import { CommonModule } from '@angular/common'; +import { ReactiveFormsModule } from '@angular/forms'; +import { By } from '@angular/platform-browser'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { DebugElement } from '@angular/core'; + +import { SubscriptionModalComponent } from './subscription-modal.component'; + +// Import mocks +import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock'; +import { findByEPersonAndDsoRes, findByEPersonAndDsoResEmpty } from '../../../testing/subscriptions-data.mock'; +import { ItemInfo } from '../../../testing/relationships-mocks'; + +// Import utils +import { NotificationsService } from '../../../notifications/notifications.service'; +import { SubscriptionService } from '../../subscription.service'; + +import { createSuccessfulRemoteDataObject$ } from '../../../remote-data.utils'; + + +describe('SubscriptionModalComponent', () => { + let component: SubscriptionModalComponent; + let fixture: ComponentFixture; + let de: DebugElement; + + let subscriptionServiceStub; + const notificationServiceStub = { + notificationWithAnchor() { + return true; + } + }; + + + describe('when empty subscriptions', () => { + + beforeEach(async () => { + + subscriptionServiceStub = jasmine.createSpyObj('SubscriptionService', { + getSubscriptionByPersonDSO: observableOf(findByEPersonAndDsoResEmpty), + createSubscription: createSuccessfulRemoteDataObject$({}), + updateSubscription: createSuccessfulRemoteDataObject$({}), + }); + + await TestBed.configureTestingModule({ + imports: [ + CommonModule, + NgbModule, + ReactiveFormsModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + declarations: [SubscriptionModalComponent], + providers: [ + { provide: ComponentFixtureAutoDetect, useValue: true }, + { provide: NotificationsService, useValue: notificationServiceStub }, + { provide: SubscriptionService, useValue: subscriptionServiceStub }, + ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SubscriptionModalComponent); + component = fixture.componentInstance; + component.ePersonId = 'testid123'; + component.dso = ItemInfo.payload; + de = fixture.debugElement; + + await fixture.whenStable(); + await fixture.whenRenderingDone(); + + fixture.detectChanges(); + + }); + + it('should be no table', () => { + expect(de.query(By.css('table'))).toBeNull(); + }); + + it('should show empty form', () => { + expect(de.query(By.css('form'))).toBeTruthy(); + }); + + it('should show form with empty checkboxes', () => { + expect(de.query(By.css('#checkbox-0'))?.nativeElement?.checked).toEqual(false); + expect(de.query(By.css('#checkbox-1'))?.nativeElement?.checked).toEqual(false); + expect(de.query(By.css('#checkbox-2'))?.nativeElement?.checked).toEqual(false); + }); + + }); + + + describe('when we have subscriptions', () => { + + beforeEach(async () => { + + subscriptionServiceStub = jasmine.createSpyObj('SubscriptionService', { + getSubscriptionByPersonDSO: observableOf(findByEPersonAndDsoRes), + createSubscription: createSuccessfulRemoteDataObject$({}), + updateSubscription: createSuccessfulRemoteDataObject$({}), + }); + + await TestBed.configureTestingModule({ + imports: [ + CommonModule, + NgbModule, + ReactiveFormsModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + declarations: [SubscriptionModalComponent], + providers: [ + { provide: ComponentFixtureAutoDetect, useValue: true }, + { provide: NotificationsService, useValue: notificationServiceStub }, + { provide: SubscriptionService, useValue: subscriptionServiceStub }, + ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SubscriptionModalComponent); + component = fixture.componentInstance; + component.ePersonId = 'testid123'; + component.dso = ItemInfo.payload; + de = fixture.debugElement; + await fixture.whenStable(); + await fixture.whenRenderingDone(); + + fixture.detectChanges(); + + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should render 2 subscriptions', () => { + expect(de.queryAll(By.css('tbody > tr')).length).toEqual(2); + }); + + it('should show no form', () => { + expect(de.query(By.css('form'))).toBeNull(); + }); + + it('should have 2 edit buttons', () => { + expect(de.queryAll(By.css('.btn-outline-primary')).length).toEqual(2); + }); + + it('should have 2 delete buttons', () => { + expect(de.queryAll(By.css('.btn-outline-danger')).length).toEqual(2); + }); + + describe('When creating new subscription', () => { + + beforeEach(() => { + // add button click + const button = de.query(By.css('.btn-success')).nativeElement; + button.click(); + }); + + + it('should show form when add button click event', () => { + expect(de.query(By.css('form'))).toBeTruthy(); + }); + + it('should show form with empty checkboxes', () => { + expect(de.query(By.css('#checkbox-0'))?.nativeElement?.checked).toEqual(false); + expect(de.query(By.css('#checkbox-1'))?.nativeElement?.checked).toEqual(false); + expect(de.query(By.css('#checkbox-2'))?.nativeElement?.checked).toEqual(false); + }); + + it('should call create request when submit click event', () => { + const checkbox = de.query(By.css('#checkbox-2')).nativeElement; + checkbox.click(); + + const button = de.queryAll(By.css('.btn-success'))[1].nativeElement; + button.click(); + expect(subscriptionServiceStub.createSubscription).toHaveBeenCalled(); + }); + + }); + + + describe('When updating subscription', () => { + + beforeEach(() => { + // edit button click + const button = de.query(By.css('.btn-outline-primary')).nativeElement; + button.click(); + }); + + it('should show form when edit button click event', () => { + expect(de.query(By.css('form'))).toBeTruthy(); + }); + + it('should show form with empty checkboxes', () => { + expect(de.query(By.css('#checkbox-0'))?.nativeElement?.checked).toEqual(false); + expect(de.query(By.css('#checkbox-1'))?.nativeElement?.checked).toEqual(true); + expect(de.query(By.css('#checkbox-2'))?.nativeElement?.checked).toEqual(false); + }); + + it('should call update request when submit click event', () => { + const button = de.queryAll(By.css('.btn-success'))[1].nativeElement; + button.click(); + expect(subscriptionServiceStub.updateSubscription).toHaveBeenCalled(); + }); + + }); + + }); + +}); diff --git a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts new file mode 100644 index 0000000000..c249cc592b --- /dev/null +++ b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts @@ -0,0 +1,306 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +import { + AbstractControl, + FormArray, + FormBuilder, + FormControl, + FormGroup, + ValidatorFn, + Validators +} from '@angular/forms'; + +import { Subscription } from '../../models/subscription.model'; + +import { BehaviorSubject, Observable } from 'rxjs'; + +import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; + +import { SubscriptionService } from '../../subscription.service'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { NotificationType } from '../../../notifications/models/notification-type'; +import { NotificationOptions } from '../../../notifications/models/notification-options.model'; + +import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; + +import { PaginatedList } from '../../../../core/data/paginated-list.model'; + +import { hasValue } from '../../../empty.util'; +import { ConfirmationModalComponent } from '../../../confirmation-modal/confirmation-modal.component'; + +import { filter, map, switchMap, take, tap } from 'rxjs/operators'; +import { NoContent } from '../../../../core/shared/NoContent.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; +import { EPerson } from '../../../../core/eperson/models/eperson.model'; +import { AuthService } from '../../../../core/auth/auth.service'; + +@Component({ + selector: 'ds-subscription-modal', + templateUrl: './subscription-modal.component.html', + styleUrls: ['./subscription-modal.component.scss'] +}) +export class SubscriptionModalComponent implements OnInit { + + /** + * DSpaceObject of which to get the subscriptions + */ + @Input() dso: DSpaceObject; + + /** + * List of subscription for the dso object and eperson relation + */ + subscriptions: Subscription[]; + + /** + * A boolean representing if a request operation is pending + * @type {BehaviorSubject} + */ + public processing$ = new BehaviorSubject(false); + + /** + * Reactive form group that will be used to add subscriptions + */ + subscriptionForm: FormGroup; + + /** + * Used to show validation errors when user submits + */ + submitted = false; + + ePersonId$: Observable; + + /** + * Types of subscription to be shown on select + */ + subscriptionTypes = [ 'content', 'statistics' ]; + + /** + * Frequencies to be shown as checkboxes + */ + frequencies = [ 'D', 'M', 'W' ]; + + constructor( + private formBuilder: FormBuilder, + private modalService: NgbModal, + private notificationsService: NotificationsService, + private subscriptionService: SubscriptionService, + public activeModal: NgbActiveModal, + private authService: AuthService, + ) { + } + + /** + * When component starts initialize starting functionality + */ + ngOnInit(): void { + + this.ePersonId$ = this.authService.getAuthenticatedUserFromStore().pipe( + take(1), + map((ePerson) => ePerson.uuid), + ); + + this.subscriptionForm = this.formBuilder.group({}); + + for (let f of this.frequencies) { + this.subscriptionForm.addControl(f, this.formBuilder.control(false)); + } + + // TODO iterate over subscription types + + /*this.subscriptionForm = this.formBuilder.group({}); + for (let t of this.subscriptionTypes) { + this.subscriptionForm.addControl(t, this.formBuilder.group({})); + for (let f of this.frequencies) { + this.subscriptionForm[t].addControl(f, this.formBuilder.control(false)); + } + }*/ + + + this.initSubscription(); + + } + + /** + * Get subscription for the eperson & dso object relation + * If no subscription start with an empty form + */ + initSubscription(): void { + this.processing$.next(true); + this.ePersonId$.pipe( + tap(console.log), + switchMap((ePersonId: string) => this.getSubscription(ePersonId, this.dso?.uuid)), + getFirstSucceededRemoteDataPayload(), + ).subscribe({ + next: (res: PaginatedList) => { + if (res.pageInfo.totalElements > 0) { + this.subscriptions = res.page; + + // TODO loop over subscription types + // for (let type of this.subscriptionTypes) { + const type = 'content'; // remove + const subscription = this.subscriptions.find((s) => s.subscriptionType === type); + // TODO manage multiple subscriptions with same tipe (there should be only one) + for (let parameter of subscription.subscriptionParameterList.filter((p) => p.name === 'frequency')) { + this.subscriptionForm.controls[parameter.value]?.setValue(true); + } + // } + + } + this.processing$.next(false); + }, + error: err => { + this.processing$.next(false); + } + }); + } + + /** + * Function to get subscriptions based on the eperson & dso + * + * @param ePersonId Eperson that is logged in + * @param uuid DSpaceObject id that subscriptions are related to + */ + getSubscription(ePersonId: string, uuid: string): Observable>> { + return this.subscriptionService.getSubscriptionByPersonDSO(ePersonId, uuid); + } + + + + submit() { + + // TODO + + /* + - remove subscription if no checkbox is selected + - add subscription if it does not exist + - edit subscription if it already exists + */ + + const body = { + type: 'content', + subscriptionParameterList: [] + }; + + for (let frequency of this.frequencies) { + if (this.subscriptionForm.value[frequency]) { + body.subscriptionParameterList.push( + { + name: 'frequency', + value: frequency, + } + ); + } + } + + // this.subscriptionService.createSubscription(body, this.ePersonId, this.dso.uuid).subscribe((res) => { + // // this.refresh(); + // // this.notify(); + // // this.processing$.next(false); + // }, + // err => { + // // this.processing$.next(false); + // } + // ); + + } + + + /** + * Sends request to create a new subscription, refreshes the table of subscriptions and notifies about summary page + */ + /*createForm(body): void { + this.subscriptionService.createSubscription(body, this.ePersonId, this.dso.uuid).subscribe((res) => { + this.refresh(); + this.notify(); + this.processing$.next(false); + }, + err => { + this.processing$.next(false); + } + ); + }*/ + + /** + * Sends request to update a subscription, refreshes the table of subscriptions and notifies about summary page + */ + /*updateForm(body) { + this.subscriptionService.updateSubscription(body, this.ePersonId, this.dso.uuid).subscribe((res) => { + this.refresh(); + this.notify(); + this.processing$.next(false); + }, + err => { + this.processing$.next(false); + } + ); + }*/ + + + /** + * Sends the request to delete the subscription with a specific id + */ + /*deleteSubscription(id): Observable { + return this.subscriptionService.deleteSubscription(id); + }*/ + + /** + * Creates a notification with the link to the subscription summary page + */ + /*notify(): void { + const options = new NotificationOptions(); + options.timeOut = 0; + const link = '/subscriptions'; + this.notificationsService.notificationWithAnchor( + NotificationType.Success, + options, + link, + 'context-menu.actions.subscription.notification.here-text', + 'context-menu.actions.subscription.notification.content', + 'here' + ); + }*/ + + /** + * When an action is done it will reinitialize the table and remove subscription form + */ + /*refresh(): void { + this.initSubscription(); + this.subscriptionForm = null; + this.submitted = false; + }*/ + + /** + * Returns if a specific frequency exists in the subscriptionParameterList + */ + getIsChecked(frequency): boolean { + return !!this.subscriptionForm.get('subscriptionParameterList').value.find(el => el.value === frequency.value); + } + + /** + * Deletes Subscription, show notification on success/failure & updates list + * + * @param subscription Subscription to be deleted + */ + /*deleteSubscriptionPopup(subscription: Subscription): void { + if (hasValue(subscription.id)) { + const modalRef = this.modalService.open(ConfirmationModalComponent); + modalRef.componentInstance.dso = this.dso; + modalRef.componentInstance.headerLabel = 'confirmation-modal.delete-subscription.header'; + modalRef.componentInstance.infoLabel = 'confirmation-modal.delete-subscription.info'; + modalRef.componentInstance.cancelLabel = 'confirmation-modal.delete-subscription.cancel'; + modalRef.componentInstance.confirmLabel = 'confirmation-modal.delete-subscription.confirm'; + modalRef.componentInstance.brandColor = 'danger'; + modalRef.componentInstance.confirmIcon = 'fas fa-trash'; + + modalRef.componentInstance.response.pipe( + take(1), + filter((confirm: boolean) => confirm), + switchMap(() => this.deleteSubscription(subscription.id)) + ).subscribe(() => { + this.refresh(); + }); + + } + }*/ +} diff --git a/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.html b/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.html new file mode 100644 index 0000000000..1ff192b638 --- /dev/null +++ b/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.html @@ -0,0 +1,29 @@ + + {{dso.type}} +

{{dso.name}}

+ + + {{subscription.subscriptionType}} + + + + + {{ 'subscriptions.frequency.'+ parameterList.value | translate}}, + + + + +
+ + +
+ + + + + + diff --git a/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.scss b/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.spec.ts b/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.spec.ts new file mode 100644 index 0000000000..e32112c5a0 --- /dev/null +++ b/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.spec.ts @@ -0,0 +1,120 @@ +import { ComponentFixture, ComponentFixtureAutoDetect, TestBed } from '@angular/core/testing'; + +// Import modules +import { CommonModule } from '@angular/common'; +import { ReactiveFormsModule } from '@angular/forms'; +import { BrowserModule, By } from '@angular/platform-browser'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { SharedModule } from '../../../shared.module'; +import { DebugElement } from '@angular/core'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { SubscriptionViewComponent } from './subscription-view.component'; + +// Import mocks +import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock'; +import { ItemInfo } from '../../../testing/relationships-mocks'; +import { findByEPersonAndDsoResEmpty, subscription } from '../../../testing/subscriptions-data.mock'; + +// Import utils +import { NotificationsService } from '../../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; +import { SubscriptionService } from '../../subscription.service'; +import { Subscription } from '../../models/subscription.model'; + +import { of as observableOf } from 'rxjs'; + +import { createSuccessfulRemoteDataObject$ } from '../../../remote-data.utils'; + + +describe('SubscriptionViewComponent', () => { + let component: SubscriptionViewComponent; + let fixture: ComponentFixture; + let de: DebugElement; + let modalService; + + const subscriptionServiceStub = jasmine.createSpyObj('SubscriptionService', { + getSubscriptionByPersonDSO: observableOf(findByEPersonAndDsoResEmpty), + deleteSubscription: createSuccessfulRemoteDataObject$({}), + updateSubscription: createSuccessfulRemoteDataObject$({}), + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + CommonModule, + NgbModule, + ReactiveFormsModule, + BrowserModule, + RouterTestingModule, + SharedModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + declarations: [ SubscriptionViewComponent ], + providers: [ + { provide: ComponentFixtureAutoDetect, useValue: true }, + { provide: NotificationsService, useValue: NotificationsServiceStub }, + { provide: SubscriptionService, useValue: subscriptionServiceStub }, + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SubscriptionViewComponent); + component = fixture.componentInstance; + component.eperson = 'testid123'; + component.dso = ItemInfo.payload; + component.subscription = Object.assign(new Subscription(), subscription); + de = fixture.debugElement; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should have dso object info', () => { + expect(de.query(By.css('.dso-info > span'))).toBeTruthy(); + expect(de.query(By.css('.dso-info > p > a'))).toBeTruthy(); + }); + + it('should have subscription type info', () => { + expect(de.query(By.css('.subscription-type'))).toBeTruthy(); + }); + + it('should have subscription paramenter info', () => { + expect(de.query(By.css('.subscription-parmenters > span'))).toBeTruthy(); + }); + + it('should have subscription action info', () => { + expect(de.query(By.css('.btn-outline-primary'))).toBeTruthy(); + expect(de.query(By.css('.btn-outline-danger'))).toBeTruthy(); + }); + + it('should open modal when clicked edit button', () => { + modalService = (component as any).modalService; + const modalSpy = spyOn(modalService, 'open'); + + const editBtn = de.query(By.css('.btn-outline-primary')).nativeElement; + editBtn.click(); + + expect(modalService.open).toHaveBeenCalled(); + }); + + it('should call delete function when clicked delete button', () => { + const deleteSpy = spyOn(component, 'deleteSubscriptionPopup'); + + const deleteBtn = de.query(By.css('.btn-outline-danger')).nativeElement; + deleteBtn.click(); + + expect(deleteSpy).toHaveBeenCalled(); + }); + +}); diff --git a/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.ts b/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.ts new file mode 100644 index 0000000000..cb831534b6 --- /dev/null +++ b/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.ts @@ -0,0 +1,102 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Subscription } from '../../models/subscription.model'; +import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; + +import { take } from 'rxjs/operators'; + +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { hasValue } from '../../../empty.util'; +import { ConfirmationModalComponent } from '../../../confirmation-modal/confirmation-modal.component'; +import { SubscriptionService } from '../../subscription.service'; + +@Component({ + // eslint-disable-next-line @angular-eslint/component-selector + selector: '[ds-subscription-view]', + templateUrl: './subscription-view.component.html', + styleUrls: ['./subscription-view.component.scss'] +}) +export class SubscriptionViewComponent { + + /** + * Subscription to be rendered + */ + @Input() subscription: Subscription; + + /** + * DSpaceObject of the subscription + */ + @Input() dso: DSpaceObject; + + /** + * EPerson of the subscription + */ + @Input() eperson: string; + + /** + * When an action is made emit a reload event + */ + @Output() reload = new EventEmitter(); + + /** + * Reference to NgbModal + */ + public modalRef: NgbModalRef; + + constructor( + private modalService: NgbModal, + private subscriptionService: SubscriptionService, + ) { } + + + /** + * Open modal + * + * @param content + */ + public openSubscription(content: any) { + this.modalRef = this.modalService.open(content); + } + + /** + * Return the prefix of the route to the dso object page ( e.g. "items") + */ + getPageRoutePrefix(): string { + let routePrefix; + switch (this.dso.type.toString()) { + case 'community': + routePrefix = '/communities'; + break; + case 'collection': + routePrefix = '/collections'; + break; + case 'item': + routePrefix = '/items'; + break; + } + return routePrefix; + } + + /** + * Deletes Subscription, show notification on success/failure & updates list + * @param subscription Subscription to be deleted + */ + deleteSubscriptionPopup(subscription: Subscription) { + if (hasValue(subscription.id)) { + const modalRef = this.modalService.open(ConfirmationModalComponent); + modalRef.componentInstance.dso = this.dso; + modalRef.componentInstance.headerLabel = 'confirmation-modal.delete-subscription.header'; + modalRef.componentInstance.infoLabel = 'confirmation-modal.delete-subscription.info'; + modalRef.componentInstance.cancelLabel = 'confirmation-modal.delete-subscription.cancel'; + modalRef.componentInstance.confirmLabel = 'confirmation-modal.delete-subscription.confirm'; + modalRef.componentInstance.brandColor = 'danger'; + modalRef.componentInstance.confirmIcon = 'fas fa-trash'; + modalRef.componentInstance.response.pipe(take(1)).subscribe((confirm: boolean) => { + if (confirm) { + this.subscriptionService.deleteSubscription(subscription.id).subscribe( (res) => { + this.reload.emit(); + }); + } + }); + } + } +} diff --git a/src/app/shared/subscriptions/models/subscription.model.ts b/src/app/shared/subscriptions/models/subscription.model.ts new file mode 100644 index 0000000000..b5d6977d39 --- /dev/null +++ b/src/app/shared/subscriptions/models/subscription.model.ts @@ -0,0 +1,57 @@ +import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; +import { typedObject } from '../../../core/cache/builders/build-decorators'; + +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; +import { HALLink } from '../../../core/shared/hal-link.model'; +import { SUBSCRIPTION } from './subscription.resource-type'; +import { EPerson } from '../../../core/eperson/models/eperson.model'; + + +@typedObject +@inheritSerialization(DSpaceObject) +export class Subscription extends DSpaceObject { + static type = SUBSCRIPTION; + + /** + * A string representing subscription type + */ + @autoserialize + public id: string; + + /** + * A string representing subscription type + */ + @autoserialize + public subscriptionType: string; + + /** + * An array of parameters for the subscription + */ + @autoserialize + public subscriptionParameterList: SubscriptionParameterList[]; + + /** + * The {@link HALLink}s for this Subscription + */ + @deserialize + _links: { + self: HALLink; + ePerson: HALLink; + dSpaceObject: HALLink; + }; + + /** + * The embedded ePerson & dSpaceObject for this Subscription + */ + @deserialize + _embedded: { + ePerson: EPerson; + dSpaceObject: DSpaceObject; + }; +} + +export interface SubscriptionParameterList { + id: string; + name: string; + value: string; +} diff --git a/src/app/shared/subscriptions/models/subscription.resource-type.ts b/src/app/shared/subscriptions/models/subscription.resource-type.ts new file mode 100644 index 0000000000..50098e53c5 --- /dev/null +++ b/src/app/shared/subscriptions/models/subscription.resource-type.ts @@ -0,0 +1,10 @@ +import { ResourceType } from '../../../core/shared/resource-type'; + +/** + * The resource type for Group + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ + +export const SUBSCRIPTION = new ResourceType('subscription'); diff --git a/src/app/shared/subscriptions/subscription.service.ts b/src/app/shared/subscriptions/subscription.service.ts new file mode 100644 index 0000000000..5010286e72 --- /dev/null +++ b/src/app/shared/subscriptions/subscription.service.ts @@ -0,0 +1,166 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; + +import { Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { distinctUntilChanged, filter, map, switchMap, take } from 'rxjs/operators'; + + +import { NotificationsService } from '../notifications/notifications.service'; +import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service'; +import { RequestParam } from '../../core/cache/models/request-param.model'; +import { ObjectCacheService } from '../../core/cache/object-cache.service'; +import { DSOChangeAnalyzer } from '../../core/data/dso-change-analyzer.service'; +import { PaginatedList } from '../../core/data/paginated-list.model'; +import { RemoteData } from '../../core/data/remote-data'; +import { CreateRequest, PutRequest } from '../../core/data/request.models'; +import { FindListOptions } from '../../core/data/find-list-options.model'; +import { RestRequest } from '../../core/data/rest-request.model'; + +import { RequestService } from '../../core/data/request.service'; +import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; +import { Subscription } from './models/subscription.model'; +import { dataService } from '../../core/data/base/data-service.decorator'; +import { SUBSCRIPTION } from './models/subscription.resource-type'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { NoContent } from '../../core/shared/NoContent.model'; +import { isNotEmpty, isNotEmptyOperator } from '../empty.util'; + +import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { sendRequest } from 'src/app/core/shared/request.operators'; +import { IdentifiableDataService } from '../../core/data/base/identifiable-data.service'; +import { DeleteDataImpl } from '../../core/data/base/delete-data'; +import { SearchDataImpl } from '../../core/data/base/search-data'; +import { FindAllData } from '../../core/data/base/find-all-data'; +import { followLink } from '../utils/follow-link-config.model'; + +/** + * Provides methods to retrieve subscription resources from the REST API related CRUD actions. + */ +@Injectable({ + providedIn: 'root' +}) +@dataService(SUBSCRIPTION) +export class SubscriptionService extends IdentifiableDataService { + protected findByEpersonLinkPath = 'findByEPerson'; + + private deleteData: DeleteDataImpl; + private findAllData: FindAllData; + private searchData: SearchDataImpl; + + constructor( + protected comparator: DSOChangeAnalyzer, + protected http: HttpClient, + protected notificationsService: NotificationsService, + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected nameService: DSONameService, + ) { + super('subscriptions', requestService, rdbService, objectCache, halService); + + this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); + this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint); + } + /** + * Get subscriptions for a given item or community or collection & eperson. + * + * @param eperson The eperson to search for + * @param uuid The uuid of the dsobjcet to search for + */ + getSubscriptionByPersonDSO(eperson: string, uuid: string): Observable>> { + + const optionsWithObject = Object.assign(new FindListOptions(), { + searchParams: [ + new RequestParam('dspace_object_id', uuid), + new RequestParam('eperson_id', eperson) + ] + }); + + return this.searchData.searchBy('findByEPersonAndDso', optionsWithObject, false, true); + } + + /** + * Create a subscription for a given item or community or collection. + * + * @param subscription The subscription to create + * @param ePerson The ePerson to create for + * @param uuid The uuid of the dsobjcet to create for + */ + createSubscription(subscription, ePerson: string, uuid: string): Observable> { + + return this.halService.getEndpoint(this.linkPath).pipe( + isNotEmptyOperator(), + take(1), + map((endpointUrl: string) => `${endpointUrl}?dspace_object_id=${uuid}&eperson_id=${ePerson}`), + map((endpointURL: string) => new CreateRequest(this.requestService.generateRequestId(), endpointURL, JSON.stringify(subscription))), + sendRequest(this.requestService), + switchMap((restRequest: RestRequest) => this.rdbService.buildFromRequestUUID(restRequest.uuid)), + getFirstCompletedRemoteData(), + ) as Observable>; + } + + /** + * Update a subscription for a given item or community or collection. + * + * @param subscription The subscription to update + * @param ePerson The ePerson to update for + * @param uuid The uuid of the dsobjcet to update for + */ + updateSubscription(subscription, ePerson: string, uuid: string) { + + return this.halService.getEndpoint(this.linkPath).pipe( + isNotEmptyOperator(), + take(1), + map((endpointUrl: string) => `${endpointUrl}/${subscription.id}?dspace_object_id=${uuid}&eperson_id=${ePerson}`), + map((endpointURL: string) => new PutRequest(this.requestService.generateRequestId(), endpointURL, JSON.stringify(subscription))), + sendRequest(this.requestService), + switchMap((restRequest: RestRequest) => this.rdbService.buildFromRequestUUID(restRequest.uuid)), + getFirstCompletedRemoteData(), + ) as Observable>; + } + + + /** + * Deletes the subscription with a give id + * + * @param id the id of Subscription to delete + */ + deleteSubscription(id: string): Observable> { + return this.halService.getEndpoint(this.linkPath).pipe( + filter((href: string) => isNotEmpty(href)), + distinctUntilChanged(), + switchMap((endpointUrl) => this.deleteData.delete(id)), + getFirstCompletedRemoteData(), + ); + } + + /** + * Retrieves the list of subscription with {@link dSpaceObject} and {@link ePerson} + * + * @param options options for the find all request + */ + findAllSubscriptions(options?): Observable>> { + return this.findAllData.findAll(options, true, true, followLink('dSpaceObject'), followLink('ePerson')); + } + + + /** + * Retrieves the list of subscription with {@link dSpaceObject} and {@link ePerson} + * + * @param ePersonId The eperson id + * @param options The options for the find all request + */ + findByEPerson(ePersonId: string, options?: FindListOptions): Observable>> { + const optionsWithObject = Object.assign(new FindListOptions(), options, { + searchParams: [ + new RequestParam('uuid', ePersonId) + ] + }); + + return this.searchData.searchBy(this.findByEpersonLinkPath, optionsWithObject, true, true, followLink('dSpaceObject'), followLink('ePerson')); + } + +} diff --git a/src/app/shared/subscriptions/subscriptions.module.ts b/src/app/shared/subscriptions/subscriptions.module.ts new file mode 100644 index 0000000000..9b72493f67 --- /dev/null +++ b/src/app/shared/subscriptions/subscriptions.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ReactiveFormsModule } from '@angular/forms'; + +import { SubscriptionViewComponent } from './components/subscription-view/subscription-view.component'; +import { SubscriptionModalComponent } from './components/subscription-modal/subscription-modal.component'; +import { SubscriptionEditModalComponent } from './components/subscription-edit-modal/subscription-edit-modal.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { RouterModule } from '@angular/router'; + +const COMPONENTS = [ + SubscriptionViewComponent, + SubscriptionModalComponent, + SubscriptionEditModalComponent, +]; + +@NgModule({ + declarations: [ + ...COMPONENTS + ], + imports: [ + CommonModule, + ReactiveFormsModule, + TranslateModule, + RouterModule + ], + exports: [ + ...COMPONENTS + ] +}) +export class SubscriptionsModule { } diff --git a/src/app/shared/testing/notifications-service.stub.ts b/src/app/shared/testing/notifications-service.stub.ts index 154c5b4351..829b65fc1d 100644 --- a/src/app/shared/testing/notifications-service.stub.ts +++ b/src/app/shared/testing/notifications-service.stub.ts @@ -9,6 +9,9 @@ export class NotificationsServiceStub { remove = jasmine.createSpy('remove'); removeAll = jasmine.createSpy('removeAll'); + notificationWithAnchor() { + } + private getDefaultOptions(): NotificationOptions { return new NotificationOptions(); } diff --git a/src/app/shared/testing/router.stub.ts b/src/app/shared/testing/router.stub.ts index 8630e16b2e..31bee0b6fe 100644 --- a/src/app/shared/testing/router.stub.ts +++ b/src/app/shared/testing/router.stub.ts @@ -9,4 +9,10 @@ export class RouterStub { navigateByUrl(url): void { this.url = url; } + createUrlTree(commands, navigationExtras = {}) { + return '/testing-url'; + } + serializeUrl(commands, navExtras = {}) { + return '/testing-url'; + } } diff --git a/src/app/shared/testing/subscriptions-data.mock.ts b/src/app/shared/testing/subscriptions-data.mock.ts new file mode 100644 index 0000000000..971f69786e --- /dev/null +++ b/src/app/shared/testing/subscriptions-data.mock.ts @@ -0,0 +1,4467 @@ +export const findAllSubscriptionRes = { + 'type': { + 'value': 'paginated-list' + }, + 'pageInfo': { + 'elementsPerPage': 10, + 'totalElements': 10, + 'totalPages': 1, + 'currentPage': 1 + }, + '_links': { + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions?page=0&size=10' + }, + 'search': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/search' + }, + 'page': [ + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21' + }, + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22' + }, + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/23' + }, + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/24' + }, + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/25' + }, + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/26' + }, + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/27' + }, + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/30' + }, + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/31' + }, + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/32' + } + ] + }, + 'page': [ + { + 'id': 21, + 'subscriptionType': 'content', + 'subscriptionParameterList': [ + { + 'id': 77, + 'name': 'frequency', + 'value': 'D' + }, + { + 'id': 78, + 'name': 'frequency', + 'value': 'M' + } + ], + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21' + } + }, + '_embedded': { + 'ePerson': { + 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'name': 'dspacedemo+admin@gmail.com', + 'handle': null, + 'metadata': { + 'dspace.agreements.cookies': [ + { + 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.agreements.end-user': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.firstname': [ + { + 'value': 'Demo', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.lastname': [ + { + 'value': 'Site Administrator', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'netid': null, + 'lastActive': '2021-09-01T12:06:19.000+00:00', + 'canLogIn': true, + 'email': 'dspacedemo+admin@gmail.com', + 'requireCertificate': false, + 'selfRegistered': false, + 'type': 'eperson', + '_links': { + 'groups': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' + } + } + }, + 'dSpaceObject': { + 'id': '092b59e8-8159-4e70-98b5-93ec60bd3431', + 'uuid': '092b59e8-8159-4e70-98b5-93ec60bd3431', + 'name': 'Bollini, Andrea', + 'handle': '123456789/43', + 'metadata': { + 'creativework.datePublished': [ + { + 'value': null, + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.orcid.access-token': [ + { + 'value': 'a456a1c3-1c9e-45f8-9e11-f273fa58de2e', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.authenticated': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.orcid.refresh-token': [ + { + 'value': '13a63f03-7c49-4dad-b39b-070f73cc7ac1', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.scope': [ + { + 'value': '/authenticate', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '/read-limited', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': '/activities/update', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + }, + { + 'value': '/person/update', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 3, + 'securityLevel': 0 + } + ], + 'dspace.orcid.sync-fundings': [ + { + 'value': 'ALL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.sync-mode': [ + { + 'value': 'MANUAL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.sync-profile': [ + { + 'value': 'AFFILIATION', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'EDUCATION', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': 'BIOGRAPHICAL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + }, + { + 'value': 'IDENTIFIERS', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 3, + 'securityLevel': 0 + } + ], + 'cris.orcid.sync-projects': [ + { + 'value': 'ALL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.sync-publications': [ + { + 'value': 'ALL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.webhook': [ + { + 'value': '2021-05-26T12:47:27.971367', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.object.owner': [ + { + 'value': 'Demo Site Administrator', + 'language': null, + 'authority': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'confidence': 600, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.policy.group': [ + { + 'value': 'Administrator', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.sourceId': [ + { + 'value': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.workspace.shared': [ + { + 'value': 'fg', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'crisrp.country': [ + { + 'value': 'IT', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'crisrp.education': [ + { + 'value': 'Università degli Studi di Milano Bicocca', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'Sapienza Università di Roma', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'crisrp.education.end': [ + { + 'value': '2008', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '2003', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'crisrp.education.role': [ + { + 'value': 'Master post experience 2nd level', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'Graduate Studies - Mathematics, Physics', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'crisrp.education.start': [ + { + 'value': '2007', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '1998', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'crisrp.name.translated': [ + { + 'value': null, + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0, + 'securityLevel': 0 + } + ], + 'crisrp.site.title': [ + { + 'value': 'LinkedIn', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'GitHub', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'dc.date.accessioned': [ + { + 'value': '2020-09-14T09:36:02Z', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.date.available': [ + { + 'value': '2020-09-14T09:36:02Z', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.description.abstract': [ + { + 'value': 'I’m responsible for all the technological aspects of the company proposal, from the final solutions to tools, methodologies and technologies adopted for the production and support activities. Among my responsibilities, I define the infrastructure that best fits the project requirements. We provide support on premis on the customer data center and worldwide cloud providers. Our hosted solutions are powered by Amazon Web Services, our experience in their services allow us to offer best in class solutions, scalable and cost-optimized.\n\nI’m in charge of the planning, estimation and execution of the projects from the technical perspective, and of the preparation of technical annexes for national and international tenders.\n\nI lead the teams of software architects and developers, assuring the adoption of best practices and up-to-date technologies. I’m in charge of the scouting and integration of new technologies and products within our solutions with a particular focus on Open Source and Open Standards. I’m directly involved with open-source and domain communities to assure that our solutions remain aligned with the international trends both from the technical perspective and for the functional requirements.', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.description.provenance': [ + { + 'value': 'Made available in DSpace on 2020-09-14T09:36:02Z (GMT). No. of bitstreams: 0', + 'language': 'en', + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.identifier.uri': [ + { + 'value': 'https://dspacecris7.4science.cloud/handle/123456789/43', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.title': [ + { + 'value': 'Bollini, Andrea', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.entity.type': [ + { + 'value': 'Person', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'oairecerif.affiliation.endDate': [ + { + 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '2016', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': '2016', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + } + ], + 'oairecerif.affiliation.role': [ + { + 'value': 'CTO', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'Head of Open Source & Open Standards Strategy', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': 'Head of Open Source & Open Standards Strategy', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + } + ], + 'oairecerif.affiliation.startDate': [ + { + 'value': '2016', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '2012', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': '2012', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + } + ], + 'oairecerif.identifier.url': [ + { + 'value': 'https://www.linkedin.com/in/andreabollini/', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'https://github.com/abollini', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'oairecerif.person.affiliation': [ + { + 'value': '4Science', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'CINECA', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': 'CINECA', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + } + ], + 'oairecerif.person.gender': [ + { + 'value': 'm', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.affiliation.name': [ + { + 'value': '4Science', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.email': [ + { + 'value': 'andrea.bollini@4science.it', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.familyName': [ + { + 'value': 'Bollini', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.givenName': [ + { + 'value': 'Andrea', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.identifier.orcid': [ + { + 'value': '0000-0003-0864-8867', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.identifier.scopus-author-id': [ + { + 'value': '55484808800', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.jobTitle': [ + { + 'value': 'CTO', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.knowsLanguage': [ + { + 'value': 'en', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'en', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ] + }, + 'inArchive': true, + 'discoverable': true, + 'withdrawn': false, + 'lastModified': '2021-08-23T10:44:57.768+00:00', + 'entityType': 'Person', + 'type': 'item', + '_links': { + 'bundles': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/bundles' + }, + 'mappedCollections': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/mappedCollections' + }, + 'owningCollection': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/owningCollection' + }, + 'relationships': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/relationships' + }, + 'version': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/version' + }, + 'templateItemOf': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/templateItemOf' + }, + 'metrics': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/metrics' + }, + 'thumbnail': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/thumbnail' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431' + } + } + } + }, + 'type': 'subscription' + }, + { + 'id': 22, + 'subscriptionType': 'content', + 'subscriptionParameterList': [ + { + 'id': 138, + 'name': 'frequency', + 'value': 'D' + } + ], + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22' + } + }, + '_embedded': { + 'ePerson': { + 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'name': 'dspacedemo+admin@gmail.com', + 'handle': null, + 'metadata': { + 'dspace.agreements.cookies': [ + { + 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.agreements.end-user': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.firstname': [ + { + 'value': 'Demo', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.lastname': [ + { + 'value': 'Site Administrator', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'netid': null, + 'lastActive': '2021-09-01T12:06:19.000+00:00', + 'canLogIn': true, + 'email': 'dspacedemo+admin@gmail.com', + 'requireCertificate': false, + 'selfRegistered': false, + 'type': 'eperson', + '_links': { + 'groups': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' + } + } + }, + 'dSpaceObject': { + 'id': '092b59e8-8159-4e70-98b5-93ec60bd3431', + 'uuid': '092b59e8-8159-4e70-98b5-93ec60bd3431', + 'name': 'Bollini, Andrea', + 'handle': '123456789/43', + 'metadata': { + 'creativework.datePublished': [ + { + 'value': null, + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.orcid.access-token': [ + { + 'value': 'a456a1c3-1c9e-45f8-9e11-f273fa58de2e', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.authenticated': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.orcid.refresh-token': [ + { + 'value': '13a63f03-7c49-4dad-b39b-070f73cc7ac1', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.scope': [ + { + 'value': '/authenticate', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '/read-limited', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': '/activities/update', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + }, + { + 'value': '/person/update', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 3, + 'securityLevel': 0 + } + ], + 'dspace.orcid.sync-fundings': [ + { + 'value': 'ALL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.sync-mode': [ + { + 'value': 'MANUAL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.sync-profile': [ + { + 'value': 'AFFILIATION', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'EDUCATION', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': 'BIOGRAPHICAL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + }, + { + 'value': 'IDENTIFIERS', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 3, + 'securityLevel': 0 + } + ], + 'cris.orcid.sync-projects': [ + { + 'value': 'ALL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.sync-publications': [ + { + 'value': 'ALL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.webhook': [ + { + 'value': '2021-05-26T12:47:27.971367', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.object.owner': [ + { + 'value': 'Demo Site Administrator', + 'language': null, + 'authority': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'confidence': 600, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.policy.group': [ + { + 'value': 'Administrator', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.sourceId': [ + { + 'value': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.workspace.shared': [ + { + 'value': 'fg', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'crisrp.country': [ + { + 'value': 'IT', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'crisrp.education': [ + { + 'value': 'Università degli Studi di Milano Bicocca', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'Sapienza Università di Roma', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'crisrp.education.end': [ + { + 'value': '2008', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '2003', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'crisrp.education.role': [ + { + 'value': 'Master post experience 2nd level', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'Graduate Studies - Mathematics, Physics', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'crisrp.education.start': [ + { + 'value': '2007', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '1998', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'crisrp.name.translated': [ + { + 'value': null, + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0, + 'securityLevel': 0 + } + ], + 'crisrp.site.title': [ + { + 'value': 'LinkedIn', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'GitHub', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'dc.date.accessioned': [ + { + 'value': '2020-09-14T09:36:02Z', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.date.available': [ + { + 'value': '2020-09-14T09:36:02Z', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.description.abstract': [ + { + 'value': 'I’m responsible for all the technological aspects of the company proposal, from the final solutions to tools, methodologies and technologies adopted for the production and support activities. Among my responsibilities, I define the infrastructure that best fits the project requirements. We provide support on premis on the customer data center and worldwide cloud providers. Our hosted solutions are powered by Amazon Web Services, our experience in their services allow us to offer best in class solutions, scalable and cost-optimized.\n\nI’m in charge of the planning, estimation and execution of the projects from the technical perspective, and of the preparation of technical annexes for national and international tenders.\n\nI lead the teams of software architects and developers, assuring the adoption of best practices and up-to-date technologies. I’m in charge of the scouting and integration of new technologies and products within our solutions with a particular focus on Open Source and Open Standards. I’m directly involved with open-source and domain communities to assure that our solutions remain aligned with the international trends both from the technical perspective and for the functional requirements.', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.description.provenance': [ + { + 'value': 'Made available in DSpace on 2020-09-14T09:36:02Z (GMT). No. of bitstreams: 0', + 'language': 'en', + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.identifier.uri': [ + { + 'value': 'https://dspacecris7.4science.cloud/handle/123456789/43', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.title': [ + { + 'value': 'Bollini, Andrea', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.entity.type': [ + { + 'value': 'Person', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'oairecerif.affiliation.endDate': [ + { + 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '2016', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': '2016', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + } + ], + 'oairecerif.affiliation.role': [ + { + 'value': 'CTO', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'Head of Open Source & Open Standards Strategy', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': 'Head of Open Source & Open Standards Strategy', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + } + ], + 'oairecerif.affiliation.startDate': [ + { + 'value': '2016', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '2012', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': '2012', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + } + ], + 'oairecerif.identifier.url': [ + { + 'value': 'https://www.linkedin.com/in/andreabollini/', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'https://github.com/abollini', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'oairecerif.person.affiliation': [ + { + 'value': '4Science', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'CINECA', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': 'CINECA', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + } + ], + 'oairecerif.person.gender': [ + { + 'value': 'm', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.affiliation.name': [ + { + 'value': '4Science', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.email': [ + { + 'value': 'andrea.bollini@4science.it', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.familyName': [ + { + 'value': 'Bollini', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.givenName': [ + { + 'value': 'Andrea', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.identifier.orcid': [ + { + 'value': '0000-0003-0864-8867', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.identifier.scopus-author-id': [ + { + 'value': '55484808800', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.jobTitle': [ + { + 'value': 'CTO', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.knowsLanguage': [ + { + 'value': 'en', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'en', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ] + }, + 'inArchive': true, + 'discoverable': true, + 'withdrawn': false, + 'lastModified': '2021-08-23T10:44:57.768+00:00', + 'entityType': 'Person', + 'type': 'item', + '_links': { + 'bundles': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/bundles' + }, + 'mappedCollections': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/mappedCollections' + }, + 'owningCollection': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/owningCollection' + }, + 'relationships': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/relationships' + }, + 'version': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/version' + }, + 'templateItemOf': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/templateItemOf' + }, + 'metrics': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/metrics' + }, + 'thumbnail': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/thumbnail' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431' + } + } + } + }, + 'type': 'subscription' + }, + { + 'id': 23, + 'subscriptionType': 'content', + 'subscriptionParameterList': [ + { + 'id': 80, + 'name': 'frequency', + 'value': 'D' + }, + { + 'id': 81, + 'name': 'frequency', + 'value': 'M' + } + ], + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/23/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/23/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/23' + } + }, + '_embedded': { + 'ePerson': { + 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'name': 'dspacedemo+admin@gmail.com', + 'handle': null, + 'metadata': { + 'dspace.agreements.cookies': [ + { + 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.agreements.end-user': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.firstname': [ + { + 'value': 'Demo', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.lastname': [ + { + 'value': 'Site Administrator', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'netid': null, + 'lastActive': '2021-09-01T12:06:19.000+00:00', + 'canLogIn': true, + 'email': 'dspacedemo+admin@gmail.com', + 'requireCertificate': false, + 'selfRegistered': false, + 'type': 'eperson', + '_links': { + 'groups': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' + } + } + }, + 'dSpaceObject': { + 'id': '092b59e8-8159-4e70-98b5-93ec60bd3431', + 'uuid': '092b59e8-8159-4e70-98b5-93ec60bd3431', + 'name': 'Bollini, Andrea', + 'handle': '123456789/43', + 'metadata': { + 'creativework.datePublished': [ + { + 'value': null, + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.orcid.access-token': [ + { + 'value': 'a456a1c3-1c9e-45f8-9e11-f273fa58de2e', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.authenticated': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.orcid.refresh-token': [ + { + 'value': '13a63f03-7c49-4dad-b39b-070f73cc7ac1', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.scope': [ + { + 'value': '/authenticate', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '/read-limited', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': '/activities/update', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + }, + { + 'value': '/person/update', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 3, + 'securityLevel': 0 + } + ], + 'dspace.orcid.sync-fundings': [ + { + 'value': 'ALL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.sync-mode': [ + { + 'value': 'MANUAL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.sync-profile': [ + { + 'value': 'AFFILIATION', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'EDUCATION', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': 'BIOGRAPHICAL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + }, + { + 'value': 'IDENTIFIERS', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 3, + 'securityLevel': 0 + } + ], + 'cris.orcid.sync-projects': [ + { + 'value': 'ALL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.sync-publications': [ + { + 'value': 'ALL', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.orcid.webhook': [ + { + 'value': '2021-05-26T12:47:27.971367', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.object.owner': [ + { + 'value': 'Demo Site Administrator', + 'language': null, + 'authority': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'confidence': 600, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.policy.group': [ + { + 'value': 'Administrator', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.sourceId': [ + { + 'value': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'cris.workspace.shared': [ + { + 'value': 'fg', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'crisrp.country': [ + { + 'value': 'IT', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'crisrp.education': [ + { + 'value': 'Università degli Studi di Milano Bicocca', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'Sapienza Università di Roma', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'crisrp.education.end': [ + { + 'value': '2008', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '2003', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'crisrp.education.role': [ + { + 'value': 'Master post experience 2nd level', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'Graduate Studies - Mathematics, Physics', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'crisrp.education.start': [ + { + 'value': '2007', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '1998', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'crisrp.name.translated': [ + { + 'value': null, + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0, + 'securityLevel': 0 + } + ], + 'crisrp.site.title': [ + { + 'value': 'LinkedIn', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'GitHub', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'dc.date.accessioned': [ + { + 'value': '2020-09-14T09:36:02Z', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.date.available': [ + { + 'value': '2020-09-14T09:36:02Z', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.description.abstract': [ + { + 'value': 'I’m responsible for all the technological aspects of the company proposal, from the final solutions to tools, methodologies and technologies adopted for the production and support activities. Among my responsibilities, I define the infrastructure that best fits the project requirements. We provide support on premis on the customer data center and worldwide cloud providers. Our hosted solutions are powered by Amazon Web Services, our experience in their services allow us to offer best in class solutions, scalable and cost-optimized.\n\nI’m in charge of the planning, estimation and execution of the projects from the technical perspective, and of the preparation of technical annexes for national and international tenders.\n\nI lead the teams of software architects and developers, assuring the adoption of best practices and up-to-date technologies. I’m in charge of the scouting and integration of new technologies and products within our solutions with a particular focus on Open Source and Open Standards. I’m directly involved with open-source and domain communities to assure that our solutions remain aligned with the international trends both from the technical perspective and for the functional requirements.', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.description.provenance': [ + { + 'value': 'Made available in DSpace on 2020-09-14T09:36:02Z (GMT). No. of bitstreams: 0', + 'language': 'en', + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.identifier.uri': [ + { + 'value': 'https://dspacecris7.4science.cloud/handle/123456789/43', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dc.title': [ + { + 'value': 'Bollini, Andrea', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'dspace.entity.type': [ + { + 'value': 'Person', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'oairecerif.affiliation.endDate': [ + { + 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '2016', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': '2016', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + } + ], + 'oairecerif.affiliation.role': [ + { + 'value': 'CTO', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'Head of Open Source & Open Standards Strategy', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': 'Head of Open Source & Open Standards Strategy', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + } + ], + 'oairecerif.affiliation.startDate': [ + { + 'value': '2016', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': '2012', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': '2012', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + } + ], + 'oairecerif.identifier.url': [ + { + 'value': 'https://www.linkedin.com/in/andreabollini/', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'https://github.com/abollini', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ], + 'oairecerif.person.affiliation': [ + { + 'value': '4Science', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'CINECA', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + }, + { + 'value': 'CINECA', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 2, + 'securityLevel': 0 + } + ], + 'oairecerif.person.gender': [ + { + 'value': 'm', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.affiliation.name': [ + { + 'value': '4Science', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.email': [ + { + 'value': 'andrea.bollini@4science.it', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.familyName': [ + { + 'value': 'Bollini', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.givenName': [ + { + 'value': 'Andrea', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.identifier.orcid': [ + { + 'value': '0000-0003-0864-8867', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.identifier.scopus-author-id': [ + { + 'value': '55484808800', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.jobTitle': [ + { + 'value': 'CTO', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + } + ], + 'person.knowsLanguage': [ + { + 'value': 'en', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 0, + 'securityLevel': 0 + }, + { + 'value': 'en', + 'language': null, + 'authority': null, + 'confidence': 0, + 'place': 1, + 'securityLevel': 0 + } + ] + }, + 'inArchive': true, + 'discoverable': true, + 'withdrawn': false, + 'lastModified': '2021-08-23T10:44:57.768+00:00', + 'entityType': 'Person', + 'type': 'item', + '_links': { + 'bundles': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/bundles' + }, + 'mappedCollections': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/mappedCollections' + }, + 'owningCollection': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/owningCollection' + }, + 'relationships': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/relationships' + }, + 'version': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/version' + }, + 'templateItemOf': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/templateItemOf' + }, + 'metrics': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/metrics' + }, + 'thumbnail': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/thumbnail' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431' + } + } + } + }, + 'type': 'subscription' + }, + { + 'id': 24, + 'subscriptionType': 'content', + 'subscriptionParameterList': [ + { + 'id': 82, + 'name': 'frequency', + 'value': 'D' + }, + { + 'id': 83, + 'name': 'frequency', + 'value': 'M' + } + ], + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/24/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/24/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/24' + } + }, + '_embedded': { + 'ePerson': { + 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'name': 'dspacedemo+admin@gmail.com', + 'handle': null, + 'metadata': { + 'dspace.agreements.cookies': [ + { + 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.agreements.end-user': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.firstname': [ + { + 'value': 'Demo', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.lastname': [ + { + 'value': 'Site Administrator', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'netid': null, + 'lastActive': '2021-09-01T12:06:19.000+00:00', + 'canLogIn': true, + 'email': 'dspacedemo+admin@gmail.com', + 'requireCertificate': false, + 'selfRegistered': false, + 'type': 'eperson', + '_links': { + 'groups': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' + } + } + }, + 'dSpaceObject': { + 'id': '90f3b9ce-db65-479c-90d7-8c794abf942c', + 'uuid': '90f3b9ce-db65-479c-90d7-8c794abf942c', + 'name': 'DSpace-CRIS', + 'handle': '123456789/34', + 'metadata': { + 'crispj.coinvestigators': [ + { + 'value': 'Mornati, Susanna', + 'language': null, + 'authority': '1325093a-1ef4-4d87-920d-02ce544fea00', + 'confidence': 600, + 'place': 0 + }, + { + 'value': 'Lombardi, Corrado', + 'language': null, + 'authority': 'b5ad6864-012d-4989-8e0d-4acfa1156fd9', + 'confidence': 600, + 'place': 1 + } + ], + 'crispj.coordinator': [ + { + 'value': '4Science', + 'language': null, + 'authority': 'a14ba215-c0f0-4b74-b21a-06359bfabd45', + 'confidence': 600, + 'place': 0 + } + ], + 'crispj.investigator': [ + { + 'value': 'Bollini, Andrea', + 'language': null, + 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', + 'confidence': 600, + 'place': 0 + } + ], + 'dc.date.accessioned': [ + { + 'value': '2020-09-05T16:33:33Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.date.available': [ + { + 'value': '2020-09-05T16:33:33Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.abstract': [ + { + 'value': 'DSpace-CRIS is the first free open-source extension of DSpace for the Research Data and Information Management ever developed. Differently from other (commercial) CRIS/RIMS (star), DSpace-CRIS has the institutional repository as its core component, providing high visibility on the web to all the collected information and objects. DSpace-CRIS broadens DSpace functionality and expands its data model while remaining aligned with its code base. \n\nDSpace-CRIS adopts/is compliant with international standards and practices to facilitate interoperability and data transfer:\n\n- ORCID API v2 (complete compliance including pull/push of info for profiles, publications, projects). ORCID API v3 compliance is being released.\n\n- Signposting and ResourceSync (which implement COAR NGR Recommended Behaviors)\n\n- OpenAIRE Guidelines for Literature Repository Managers v4, for Data Archives, for CRIS Managers v1.1.1 (based on CERIF, released Nov. 2019)\n\n- PlanS (by Coalition S)\n\n- FAIR principles\n\nThe main characteristic of DSpace-CRIS is its flexible data model, which allows you to collect and manage research data and information typical of a CRIS system, to define entities and attributes with their reciprocal links. If you would just want to enhance the management of authors, provide name variants and IDs such as the ORCiD, exploit the varied ecosystem of persistent identifiers, link researchers to projects, awards, etc., DSpace-CRIS flexible data model can support this without aggravating the management burden of a normal institutional repository, while giving a great added value. Besides, it has useful features such as the collaboration network graph, aggregated (by researcher, by department) bibliometrics and statistics with graphic reporting, CVs and bibliographies, integration with ORCiD API v.3 and much more, you can explore them vie the menu items here on the left. \n\nIts flexibility allows to configure different data models and metadata schemas, providing the community with new and creative uses of DSpace, such as DSpace-GLAM (Galleries, Libraries, Archives and Museums) for the Cultural Heritage.', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.provenance': [ + { + 'value': 'Made available in DSpace on 2020-09-05T16:33:33Z (GMT). No. of bitstreams: 1\nlayers.png: 295537 bytes, checksum: 001e24b05d2c5d6cd76e88580f10cb9b (MD5)', + 'language': 'en', + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.identifier.uri': [ + { + 'value': 'https://dspacecris7.4science.cloud/handle/123456789/34', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.subject': [ + { + 'value': 'cerif', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + }, + { + 'value': 'datamanagement', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 1 + }, + { + 'value': 'opensource', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 2 + } + ], + 'dc.title': [ + { + 'value': 'DSpace-CRIS', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.type': [ + { + 'value': 'applied research', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.entity.type': [ + { + 'value': 'Project', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.acronym': [ + { + 'value': 'DSC', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.oamandate': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.oamandate.url': [ + { + 'value': 'https://www.4science.it/en/open-source/', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.project.startDate': [ + { + 'value': '2009-04', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.project.status': [ + { + 'value': 'ongoing', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'inArchive': true, + 'discoverable': true, + 'withdrawn': false, + 'lastModified': '2021-08-02T14:19:24.927+00:00', + 'entityType': 'Project', + 'type': 'item', + '_links': { + 'bundles': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/bundles' + }, + 'mappedCollections': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/mappedCollections' + }, + 'owningCollection': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/owningCollection' + }, + 'relationships': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/relationships' + }, + 'version': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/version' + }, + 'templateItemOf': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/templateItemOf' + }, + 'metrics': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/metrics' + }, + 'thumbnail': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/thumbnail' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c' + } + } + } + }, + 'type': 'subscription' + }, + { + 'id': 25, + 'subscriptionType': 'content', + 'subscriptionParameterList': [ + { + 'id': 84, + 'name': 'frequency', + 'value': 'D' + }, + { + 'id': 85, + 'name': 'frequency', + 'value': 'M' + }, + { + 'id': 86, + 'name': 'frequency', + 'value': 'W' + } + ], + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/25/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/25/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/25' + } + }, + '_embedded': { + 'ePerson': { + 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'name': 'dspacedemo+admin@gmail.com', + 'handle': null, + 'metadata': { + 'dspace.agreements.cookies': [ + { + 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.agreements.end-user': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.firstname': [ + { + 'value': 'Demo', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.lastname': [ + { + 'value': 'Site Administrator', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'netid': null, + 'lastActive': '2021-09-01T12:06:19.000+00:00', + 'canLogIn': true, + 'email': 'dspacedemo+admin@gmail.com', + 'requireCertificate': false, + 'selfRegistered': false, + 'type': 'eperson', + '_links': { + 'groups': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' + } + } + }, + 'dSpaceObject': { + 'id': '90f3b9ce-db65-479c-90d7-8c794abf942c', + 'uuid': '90f3b9ce-db65-479c-90d7-8c794abf942c', + 'name': 'DSpace-CRIS', + 'handle': '123456789/34', + 'metadata': { + 'crispj.coinvestigators': [ + { + 'value': 'Mornati, Susanna', + 'language': null, + 'authority': '1325093a-1ef4-4d87-920d-02ce544fea00', + 'confidence': 600, + 'place': 0 + }, + { + 'value': 'Lombardi, Corrado', + 'language': null, + 'authority': 'b5ad6864-012d-4989-8e0d-4acfa1156fd9', + 'confidence': 600, + 'place': 1 + } + ], + 'crispj.coordinator': [ + { + 'value': '4Science', + 'language': null, + 'authority': 'a14ba215-c0f0-4b74-b21a-06359bfabd45', + 'confidence': 600, + 'place': 0 + } + ], + 'crispj.investigator': [ + { + 'value': 'Bollini, Andrea', + 'language': null, + 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', + 'confidence': 600, + 'place': 0 + } + ], + 'dc.date.accessioned': [ + { + 'value': '2020-09-05T16:33:33Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.date.available': [ + { + 'value': '2020-09-05T16:33:33Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.abstract': [ + { + 'value': 'DSpace-CRIS is the first free open-source extension of DSpace for the Research Data and Information Management ever developed. Differently from other (commercial) CRIS/RIMS (star), DSpace-CRIS has the institutional repository as its core component, providing high visibility on the web to all the collected information and objects. DSpace-CRIS broadens DSpace functionality and expands its data model while remaining aligned with its code base. \n\nDSpace-CRIS adopts/is compliant with international standards and practices to facilitate interoperability and data transfer:\n\n- ORCID API v2 (complete compliance including pull/push of info for profiles, publications, projects). ORCID API v3 compliance is being released.\n\n- Signposting and ResourceSync (which implement COAR NGR Recommended Behaviors)\n\n- OpenAIRE Guidelines for Literature Repository Managers v4, for Data Archives, for CRIS Managers v1.1.1 (based on CERIF, released Nov. 2019)\n\n- PlanS (by Coalition S)\n\n- FAIR principles\n\nThe main characteristic of DSpace-CRIS is its flexible data model, which allows you to collect and manage research data and information typical of a CRIS system, to define entities and attributes with their reciprocal links. If you would just want to enhance the management of authors, provide name variants and IDs such as the ORCiD, exploit the varied ecosystem of persistent identifiers, link researchers to projects, awards, etc., DSpace-CRIS flexible data model can support this without aggravating the management burden of a normal institutional repository, while giving a great added value. Besides, it has useful features such as the collaboration network graph, aggregated (by researcher, by department) bibliometrics and statistics with graphic reporting, CVs and bibliographies, integration with ORCiD API v.3 and much more, you can explore them vie the menu items here on the left. \n\nIts flexibility allows to configure different data models and metadata schemas, providing the community with new and creative uses of DSpace, such as DSpace-GLAM (Galleries, Libraries, Archives and Museums) for the Cultural Heritage.', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.provenance': [ + { + 'value': 'Made available in DSpace on 2020-09-05T16:33:33Z (GMT). No. of bitstreams: 1\nlayers.png: 295537 bytes, checksum: 001e24b05d2c5d6cd76e88580f10cb9b (MD5)', + 'language': 'en', + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.identifier.uri': [ + { + 'value': 'https://dspacecris7.4science.cloud/handle/123456789/34', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.subject': [ + { + 'value': 'cerif', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + }, + { + 'value': 'datamanagement', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 1 + }, + { + 'value': 'opensource', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 2 + } + ], + 'dc.title': [ + { + 'value': 'DSpace-CRIS', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.type': [ + { + 'value': 'applied research', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.entity.type': [ + { + 'value': 'Project', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.acronym': [ + { + 'value': 'DSC', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.oamandate': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.oamandate.url': [ + { + 'value': 'https://www.4science.it/en/open-source/', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.project.startDate': [ + { + 'value': '2009-04', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.project.status': [ + { + 'value': 'ongoing', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'inArchive': true, + 'discoverable': true, + 'withdrawn': false, + 'lastModified': '2021-08-02T14:19:24.927+00:00', + 'entityType': 'Project', + 'type': 'item', + '_links': { + 'bundles': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/bundles' + }, + 'mappedCollections': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/mappedCollections' + }, + 'owningCollection': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/owningCollection' + }, + 'relationships': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/relationships' + }, + 'version': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/version' + }, + 'templateItemOf': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/templateItemOf' + }, + 'metrics': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/metrics' + }, + 'thumbnail': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/thumbnail' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c' + } + } + } + }, + 'type': 'subscription' + }, + { + 'id': 26, + 'subscriptionType': 'content', + 'subscriptionParameterList': [ + { + 'id': 87, + 'name': 'frequency', + 'value': 'D' + }, + { + 'id': 88, + 'name': 'frequency', + 'value': 'M' + } + ], + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/26/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/26/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/26' + } + }, + '_embedded': { + 'ePerson': { + 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'name': 'dspacedemo+admin@gmail.com', + 'handle': null, + 'metadata': { + 'dspace.agreements.cookies': [ + { + 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.agreements.end-user': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.firstname': [ + { + 'value': 'Demo', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.lastname': [ + { + 'value': 'Site Administrator', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'netid': null, + 'lastActive': '2021-09-01T12:06:19.000+00:00', + 'canLogIn': true, + 'email': 'dspacedemo+admin@gmail.com', + 'requireCertificate': false, + 'selfRegistered': false, + 'type': 'eperson', + '_links': { + 'groups': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' + } + } + }, + 'dSpaceObject': { + 'id': '90f3b9ce-db65-479c-90d7-8c794abf942c', + 'uuid': '90f3b9ce-db65-479c-90d7-8c794abf942c', + 'name': 'DSpace-CRIS', + 'handle': '123456789/34', + 'metadata': { + 'crispj.coinvestigators': [ + { + 'value': 'Mornati, Susanna', + 'language': null, + 'authority': '1325093a-1ef4-4d87-920d-02ce544fea00', + 'confidence': 600, + 'place': 0 + }, + { + 'value': 'Lombardi, Corrado', + 'language': null, + 'authority': 'b5ad6864-012d-4989-8e0d-4acfa1156fd9', + 'confidence': 600, + 'place': 1 + } + ], + 'crispj.coordinator': [ + { + 'value': '4Science', + 'language': null, + 'authority': 'a14ba215-c0f0-4b74-b21a-06359bfabd45', + 'confidence': 600, + 'place': 0 + } + ], + 'crispj.investigator': [ + { + 'value': 'Bollini, Andrea', + 'language': null, + 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', + 'confidence': 600, + 'place': 0 + } + ], + 'dc.date.accessioned': [ + { + 'value': '2020-09-05T16:33:33Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.date.available': [ + { + 'value': '2020-09-05T16:33:33Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.abstract': [ + { + 'value': 'DSpace-CRIS is the first free open-source extension of DSpace for the Research Data and Information Management ever developed. Differently from other (commercial) CRIS/RIMS (star), DSpace-CRIS has the institutional repository as its core component, providing high visibility on the web to all the collected information and objects. DSpace-CRIS broadens DSpace functionality and expands its data model while remaining aligned with its code base. \n\nDSpace-CRIS adopts/is compliant with international standards and practices to facilitate interoperability and data transfer:\n\n- ORCID API v2 (complete compliance including pull/push of info for profiles, publications, projects). ORCID API v3 compliance is being released.\n\n- Signposting and ResourceSync (which implement COAR NGR Recommended Behaviors)\n\n- OpenAIRE Guidelines for Literature Repository Managers v4, for Data Archives, for CRIS Managers v1.1.1 (based on CERIF, released Nov. 2019)\n\n- PlanS (by Coalition S)\n\n- FAIR principles\n\nThe main characteristic of DSpace-CRIS is its flexible data model, which allows you to collect and manage research data and information typical of a CRIS system, to define entities and attributes with their reciprocal links. If you would just want to enhance the management of authors, provide name variants and IDs such as the ORCiD, exploit the varied ecosystem of persistent identifiers, link researchers to projects, awards, etc., DSpace-CRIS flexible data model can support this without aggravating the management burden of a normal institutional repository, while giving a great added value. Besides, it has useful features such as the collaboration network graph, aggregated (by researcher, by department) bibliometrics and statistics with graphic reporting, CVs and bibliographies, integration with ORCiD API v.3 and much more, you can explore them vie the menu items here on the left. \n\nIts flexibility allows to configure different data models and metadata schemas, providing the community with new and creative uses of DSpace, such as DSpace-GLAM (Galleries, Libraries, Archives and Museums) for the Cultural Heritage.', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.provenance': [ + { + 'value': 'Made available in DSpace on 2020-09-05T16:33:33Z (GMT). No. of bitstreams: 1\nlayers.png: 295537 bytes, checksum: 001e24b05d2c5d6cd76e88580f10cb9b (MD5)', + 'language': 'en', + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.identifier.uri': [ + { + 'value': 'https://dspacecris7.4science.cloud/handle/123456789/34', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.subject': [ + { + 'value': 'cerif', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + }, + { + 'value': 'datamanagement', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 1 + }, + { + 'value': 'opensource', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 2 + } + ], + 'dc.title': [ + { + 'value': 'DSpace-CRIS', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.type': [ + { + 'value': 'applied research', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.entity.type': [ + { + 'value': 'Project', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.acronym': [ + { + 'value': 'DSC', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.oamandate': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.oamandate.url': [ + { + 'value': 'https://www.4science.it/en/open-source/', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.project.startDate': [ + { + 'value': '2009-04', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.project.status': [ + { + 'value': 'ongoing', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'inArchive': true, + 'discoverable': true, + 'withdrawn': false, + 'lastModified': '2021-08-02T14:19:24.927+00:00', + 'entityType': 'Project', + 'type': 'item', + '_links': { + 'bundles': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/bundles' + }, + 'mappedCollections': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/mappedCollections' + }, + 'owningCollection': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/owningCollection' + }, + 'relationships': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/relationships' + }, + 'version': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/version' + }, + 'templateItemOf': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/templateItemOf' + }, + 'metrics': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/metrics' + }, + 'thumbnail': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/thumbnail' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c' + } + } + } + }, + 'type': 'subscription' + }, + { + 'id': 27, + 'subscriptionType': 'content', + 'subscriptionParameterList': [ + { + 'id': 89, + 'name': 'frequency', + 'value': 'D' + }, + { + 'id': 90, + 'name': 'frequency', + 'value': 'M' + }, + { + 'id': 91, + 'name': 'frequency', + 'value': 'W' + } + ], + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/27/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/27/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/27' + } + }, + '_embedded': { + 'ePerson': { + 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'name': 'dspacedemo+admin@gmail.com', + 'handle': null, + 'metadata': { + 'dspace.agreements.cookies': [ + { + 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.agreements.end-user': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.firstname': [ + { + 'value': 'Demo', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.lastname': [ + { + 'value': 'Site Administrator', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'netid': null, + 'lastActive': '2021-09-01T12:06:19.000+00:00', + 'canLogIn': true, + 'email': 'dspacedemo+admin@gmail.com', + 'requireCertificate': false, + 'selfRegistered': false, + 'type': 'eperson', + '_links': { + 'groups': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' + } + } + }, + 'dSpaceObject': { + 'id': '90f3b9ce-db65-479c-90d7-8c794abf942c', + 'uuid': '90f3b9ce-db65-479c-90d7-8c794abf942c', + 'name': 'DSpace-CRIS', + 'handle': '123456789/34', + 'metadata': { + 'crispj.coinvestigators': [ + { + 'value': 'Mornati, Susanna', + 'language': null, + 'authority': '1325093a-1ef4-4d87-920d-02ce544fea00', + 'confidence': 600, + 'place': 0 + }, + { + 'value': 'Lombardi, Corrado', + 'language': null, + 'authority': 'b5ad6864-012d-4989-8e0d-4acfa1156fd9', + 'confidence': 600, + 'place': 1 + } + ], + 'crispj.coordinator': [ + { + 'value': '4Science', + 'language': null, + 'authority': 'a14ba215-c0f0-4b74-b21a-06359bfabd45', + 'confidence': 600, + 'place': 0 + } + ], + 'crispj.investigator': [ + { + 'value': 'Bollini, Andrea', + 'language': null, + 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', + 'confidence': 600, + 'place': 0 + } + ], + 'dc.date.accessioned': [ + { + 'value': '2020-09-05T16:33:33Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.date.available': [ + { + 'value': '2020-09-05T16:33:33Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.abstract': [ + { + 'value': 'DSpace-CRIS is the first free open-source extension of DSpace for the Research Data and Information Management ever developed. Differently from other (commercial) CRIS/RIMS (star), DSpace-CRIS has the institutional repository as its core component, providing high visibility on the web to all the collected information and objects. DSpace-CRIS broadens DSpace functionality and expands its data model while remaining aligned with its code base. \n\nDSpace-CRIS adopts/is compliant with international standards and practices to facilitate interoperability and data transfer:\n\n- ORCID API v2 (complete compliance including pull/push of info for profiles, publications, projects). ORCID API v3 compliance is being released.\n\n- Signposting and ResourceSync (which implement COAR NGR Recommended Behaviors)\n\n- OpenAIRE Guidelines for Literature Repository Managers v4, for Data Archives, for CRIS Managers v1.1.1 (based on CERIF, released Nov. 2019)\n\n- PlanS (by Coalition S)\n\n- FAIR principles\n\nThe main characteristic of DSpace-CRIS is its flexible data model, which allows you to collect and manage research data and information typical of a CRIS system, to define entities and attributes with their reciprocal links. If you would just want to enhance the management of authors, provide name variants and IDs such as the ORCiD, exploit the varied ecosystem of persistent identifiers, link researchers to projects, awards, etc., DSpace-CRIS flexible data model can support this without aggravating the management burden of a normal institutional repository, while giving a great added value. Besides, it has useful features such as the collaboration network graph, aggregated (by researcher, by department) bibliometrics and statistics with graphic reporting, CVs and bibliographies, integration with ORCiD API v.3 and much more, you can explore them vie the menu items here on the left. \n\nIts flexibility allows to configure different data models and metadata schemas, providing the community with new and creative uses of DSpace, such as DSpace-GLAM (Galleries, Libraries, Archives and Museums) for the Cultural Heritage.', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.provenance': [ + { + 'value': 'Made available in DSpace on 2020-09-05T16:33:33Z (GMT). No. of bitstreams: 1\nlayers.png: 295537 bytes, checksum: 001e24b05d2c5d6cd76e88580f10cb9b (MD5)', + 'language': 'en', + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.identifier.uri': [ + { + 'value': 'https://dspacecris7.4science.cloud/handle/123456789/34', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.subject': [ + { + 'value': 'cerif', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + }, + { + 'value': 'datamanagement', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 1 + }, + { + 'value': 'opensource', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 2 + } + ], + 'dc.title': [ + { + 'value': 'DSpace-CRIS', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.type': [ + { + 'value': 'applied research', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.entity.type': [ + { + 'value': 'Project', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.acronym': [ + { + 'value': 'DSC', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.oamandate': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.oamandate.url': [ + { + 'value': 'https://www.4science.it/en/open-source/', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.project.startDate': [ + { + 'value': '2009-04', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.project.status': [ + { + 'value': 'ongoing', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'inArchive': true, + 'discoverable': true, + 'withdrawn': false, + 'lastModified': '2021-08-02T14:19:24.927+00:00', + 'entityType': 'Project', + 'type': 'item', + '_links': { + 'bundles': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/bundles' + }, + 'mappedCollections': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/mappedCollections' + }, + 'owningCollection': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/owningCollection' + }, + 'relationships': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/relationships' + }, + 'version': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/version' + }, + 'templateItemOf': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/templateItemOf' + }, + 'metrics': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/metrics' + }, + 'thumbnail': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/thumbnail' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c' + } + } + } + }, + 'type': 'subscription' + }, + { + 'id': 30, + 'subscriptionType': 'statistics', + 'subscriptionParameterList': [ + { + 'id': 96, + 'name': 'frequency', + 'value': 'M' + }, + { + 'id': 97, + 'name': 'frequency', + 'value': 'D' + } + ], + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/30/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/30/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/30' + } + }, + '_embedded': { + 'ePerson': { + 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'name': 'dspacedemo+admin@gmail.com', + 'handle': null, + 'metadata': { + 'dspace.agreements.cookies': [ + { + 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.agreements.end-user': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.firstname': [ + { + 'value': 'Demo', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.lastname': [ + { + 'value': 'Site Administrator', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'netid': null, + 'lastActive': '2021-09-01T12:06:19.000+00:00', + 'canLogIn': true, + 'email': 'dspacedemo+admin@gmail.com', + 'requireCertificate': false, + 'selfRegistered': false, + 'type': 'eperson', + '_links': { + 'groups': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' + } + } + }, + 'dSpaceObject': { + 'id': 'b2140cd5-bfdf-4b5b-83fb-8bab4c899b40', + 'uuid': 'b2140cd5-bfdf-4b5b-83fb-8bab4c899b40', + 'name': 'DSpace administration issues: the community admin patch', + 'handle': '123456789/107', + 'metadata': { + 'dc.contributor.author': [ + { + 'value': 'Donohue, Tim', + 'language': null, + 'authority': 'fcae3ff0-6a04-4385-8cae-04a38bbe4969', + 'confidence': 600, + 'place': 0 + }, + { + 'value': 'Bollini, Andrea', + 'language': null, + 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', + 'confidence': 600, + 'place': 1 + } + ], + 'dc.date.accessioned': [ + { + 'value': '2020-12-06T22:35:52Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.date.available': [ + { + 'value': '2020-12-06T22:35:52Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.date.issued': [ + { + 'value': '2005', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.abstract': [ + { + 'value': 'A large or medium repository, but also a small repository in some special cases, needs to allow a more decentralized management of administrative activities as: creation of new communities, creation of new collections, management of submitter and workflow groups, editing of published items, access policies and so on. Until now, DSpace allows only a partial decentralization of this functionalities thought into the role of COLLECTION ADMIN. After highlighting these needs, we will introduce the new role of COMMUNITY ADMIN and the changes made to the COLLECTION ADMIN role by our patch so to fix most of the previous needs. We will talk about the \'long history\' of this patch, made for the first time by Andrea against the 1.2 series and next kept updated, bug free and XMLUI aware by Tim from the 1.4 series. This \'pass the buck\', from Andrea to Tim and again together with some other people, shows how useful is for anyone to share results, experiences and customizations with the community so to get them back improved, reducing the cost of locale maintenance, debug and bug fix. We will close the presentation with the good news of the inclusion of this patch in the DSpace codebase for both XMLUI and JSPUI interfaces and also giving some notices about possible improvements in the next future. DSpace User Group Meeting 2009', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.provenance': [ + { + 'value': 'Made available in DSpace on 2020-12-06T22:35:52Z (GMT). No. of bitstreams: 0\n Previous issue date: 2005', + 'language': 'en', + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.identifier.other': [ + { + 'value': 'od______1149::b43204759808a65356e6cc3ba285d29f', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.identifier.uri': [ + { + 'value': 'https://dspacecris7.4science.cloud/handle/123456789/107', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.language.iso': [ + { + 'value': 'en', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.source': [ + { + 'value': 'Göteborgs universitets publikationer - e-publicering och e-arkiv', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.subject': [ + { + 'value': 'dspace', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + }, + { + 'value': 'open source', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 1 + } + ], + 'dc.title': [ + { + 'value': 'DSpace administration issues: the community admin patch', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.type': [ + { + 'value': 'Controlled Vocabulary for Resource Type Genres::text::conference object', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.entity.type': [ + { + 'value': 'Publication', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.author.affiliation': [ + { + 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + }, + { + 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 1 + } + ] + }, + 'inArchive': true, + 'discoverable': true, + 'withdrawn': false, + 'lastModified': '2021-05-31T21:41:41.737+00:00', + 'entityType': 'Publication', + 'type': 'item', + '_links': { + 'bundles': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/bundles' + }, + 'mappedCollections': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/mappedCollections' + }, + 'owningCollection': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/owningCollection' + }, + 'relationships': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/relationships' + }, + 'version': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/version' + }, + 'templateItemOf': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/templateItemOf' + }, + 'metrics': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/metrics' + }, + 'thumbnail': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/thumbnail' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40' + } + } + } + }, + 'type': 'subscription' + }, + { + 'id': 31, + 'subscriptionType': 'content', + 'subscriptionParameterList': [ + { + 'id': 98, + 'name': 'frequency', + 'value': 'D' + }, + { + 'id': 99, + 'name': 'frequency', + 'value': 'M' + } + ], + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/31/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/31/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/31' + } + }, + '_embedded': { + 'ePerson': { + 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'name': 'dspacedemo+admin@gmail.com', + 'handle': null, + 'metadata': { + 'dspace.agreements.cookies': [ + { + 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.agreements.end-user': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.firstname': [ + { + 'value': 'Demo', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.lastname': [ + { + 'value': 'Site Administrator', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'netid': null, + 'lastActive': '2021-09-01T12:06:19.000+00:00', + 'canLogIn': true, + 'email': 'dspacedemo+admin@gmail.com', + 'requireCertificate': false, + 'selfRegistered': false, + 'type': 'eperson', + '_links': { + 'groups': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' + } + } + }, + 'dSpaceObject': { + 'id': 'b2140cd5-bfdf-4b5b-83fb-8bab4c899b40', + 'uuid': 'b2140cd5-bfdf-4b5b-83fb-8bab4c899b40', + 'name': 'DSpace administration issues: the community admin patch', + 'handle': '123456789/107', + 'metadata': { + 'dc.contributor.author': [ + { + 'value': 'Donohue, Tim', + 'language': null, + 'authority': 'fcae3ff0-6a04-4385-8cae-04a38bbe4969', + 'confidence': 600, + 'place': 0 + }, + { + 'value': 'Bollini, Andrea', + 'language': null, + 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', + 'confidence': 600, + 'place': 1 + } + ], + 'dc.date.accessioned': [ + { + 'value': '2020-12-06T22:35:52Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.date.available': [ + { + 'value': '2020-12-06T22:35:52Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.date.issued': [ + { + 'value': '2005', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.abstract': [ + { + 'value': 'A large or medium repository, but also a small repository in some special cases, needs to allow a more decentralized management of administrative activities as: creation of new communities, creation of new collections, management of submitter and workflow groups, editing of published items, access policies and so on. Until now, DSpace allows only a partial decentralization of this functionalities thought into the role of COLLECTION ADMIN. After highlighting these needs, we will introduce the new role of COMMUNITY ADMIN and the changes made to the COLLECTION ADMIN role by our patch so to fix most of the previous needs. We will talk about the \'long history\' of this patch, made for the first time by Andrea against the 1.2 series and next kept updated, bug free and XMLUI aware by Tim from the 1.4 series. This \'pass the buck\', from Andrea to Tim and again together with some other people, shows how useful is for anyone to share results, experiences and customizations with the community so to get them back improved, reducing the cost of locale maintenance, debug and bug fix. We will close the presentation with the good news of the inclusion of this patch in the DSpace codebase for both XMLUI and JSPUI interfaces and also giving some notices about possible improvements in the next future. DSpace User Group Meeting 2009', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.provenance': [ + { + 'value': 'Made available in DSpace on 2020-12-06T22:35:52Z (GMT). No. of bitstreams: 0\n Previous issue date: 2005', + 'language': 'en', + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.identifier.other': [ + { + 'value': 'od______1149::b43204759808a65356e6cc3ba285d29f', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.identifier.uri': [ + { + 'value': 'https://dspacecris7.4science.cloud/handle/123456789/107', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.language.iso': [ + { + 'value': 'en', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.source': [ + { + 'value': 'Göteborgs universitets publikationer - e-publicering och e-arkiv', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.subject': [ + { + 'value': 'dspace', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + }, + { + 'value': 'open source', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 1 + } + ], + 'dc.title': [ + { + 'value': 'DSpace administration issues: the community admin patch', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.type': [ + { + 'value': 'Controlled Vocabulary for Resource Type Genres::text::conference object', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.entity.type': [ + { + 'value': 'Publication', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.author.affiliation': [ + { + 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + }, + { + 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 1 + } + ] + }, + 'inArchive': true, + 'discoverable': true, + 'withdrawn': false, + 'lastModified': '2021-05-31T21:41:41.737+00:00', + 'entityType': 'Publication', + 'type': 'item', + '_links': { + 'bundles': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/bundles' + }, + 'mappedCollections': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/mappedCollections' + }, + 'owningCollection': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/owningCollection' + }, + 'relationships': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/relationships' + }, + 'version': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/version' + }, + 'templateItemOf': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/templateItemOf' + }, + 'metrics': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/metrics' + }, + 'thumbnail': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/thumbnail' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40' + } + } + } + }, + 'type': 'subscription' + }, + { + 'id': 32, + 'subscriptionType': 'content', + 'subscriptionParameterList': [ + { + 'id': 100, + 'name': 'frequency', + 'value': 'W' + } + ], + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/32/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/32/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/32' + } + }, + '_embedded': { + 'ePerson': { + 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', + 'name': 'dspacedemo+admin@gmail.com', + 'handle': null, + 'metadata': { + 'dspace.agreements.cookies': [ + { + 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.agreements.end-user': [ + { + 'value': 'true', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.firstname': [ + { + 'value': 'Demo', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.lastname': [ + { + 'value': 'Site Administrator', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ] + }, + 'netid': null, + 'lastActive': '2021-09-01T12:06:19.000+00:00', + 'canLogIn': true, + 'email': 'dspacedemo+admin@gmail.com', + 'requireCertificate': false, + 'selfRegistered': false, + 'type': 'eperson', + '_links': { + 'groups': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' + } + } + }, + 'dSpaceObject': { + 'id': 'b2140cd5-bfdf-4b5b-83fb-8bab4c899b40', + 'uuid': 'b2140cd5-bfdf-4b5b-83fb-8bab4c899b40', + 'name': 'DSpace administration issues: the community admin patch', + 'handle': '123456789/107', + 'metadata': { + 'dc.contributor.author': [ + { + 'value': 'Donohue, Tim', + 'language': null, + 'authority': 'fcae3ff0-6a04-4385-8cae-04a38bbe4969', + 'confidence': 600, + 'place': 0 + }, + { + 'value': 'Bollini, Andrea', + 'language': null, + 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', + 'confidence': 600, + 'place': 1 + } + ], + 'dc.date.accessioned': [ + { + 'value': '2020-12-06T22:35:52Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.date.available': [ + { + 'value': '2020-12-06T22:35:52Z', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.date.issued': [ + { + 'value': '2005', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.abstract': [ + { + 'value': 'A large or medium repository, but also a small repository in some special cases, needs to allow a more decentralized management of administrative activities as: creation of new communities, creation of new collections, management of submitter and workflow groups, editing of published items, access policies and so on. Until now, DSpace allows only a partial decentralization of this functionalities thought into the role of COLLECTION ADMIN. After highlighting these needs, we will introduce the new role of COMMUNITY ADMIN and the changes made to the COLLECTION ADMIN role by our patch so to fix most of the previous needs. We will talk about the \'long history\' of this patch, made for the first time by Andrea against the 1.2 series and next kept updated, bug free and XMLUI aware by Tim from the 1.4 series. This \'pass the buck\', from Andrea to Tim and again together with some other people, shows how useful is for anyone to share results, experiences and customizations with the community so to get them back improved, reducing the cost of locale maintenance, debug and bug fix. We will close the presentation with the good news of the inclusion of this patch in the DSpace codebase for both XMLUI and JSPUI interfaces and also giving some notices about possible improvements in the next future. DSpace User Group Meeting 2009', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.description.provenance': [ + { + 'value': 'Made available in DSpace on 2020-12-06T22:35:52Z (GMT). No. of bitstreams: 0\n Previous issue date: 2005', + 'language': 'en', + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.identifier.other': [ + { + 'value': 'od______1149::b43204759808a65356e6cc3ba285d29f', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.identifier.uri': [ + { + 'value': 'https://dspacecris7.4science.cloud/handle/123456789/107', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.language.iso': [ + { + 'value': 'en', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.source': [ + { + 'value': 'Göteborgs universitets publikationer - e-publicering och e-arkiv', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.subject': [ + { + 'value': 'dspace', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + }, + { + 'value': 'open source', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 1 + } + ], + 'dc.title': [ + { + 'value': 'DSpace administration issues: the community admin patch', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dc.type': [ + { + 'value': 'Controlled Vocabulary for Resource Type Genres::text::conference object', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'dspace.entity.type': [ + { + 'value': 'Publication', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'oairecerif.author.affiliation': [ + { + 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + }, + { + 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 1 + } + ] + }, + 'inArchive': true, + 'discoverable': true, + 'withdrawn': false, + 'lastModified': '2021-05-31T21:41:41.737+00:00', + 'entityType': 'Publication', + 'type': 'item', + '_links': { + 'bundles': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/bundles' + }, + 'mappedCollections': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/mappedCollections' + }, + 'owningCollection': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/owningCollection' + }, + 'relationships': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/relationships' + }, + 'version': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/version' + }, + 'templateItemOf': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/templateItemOf' + }, + 'metrics': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/metrics' + }, + 'thumbnail': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/thumbnail' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40' + } + } + } + }, + 'type': 'subscription' + } + ] +}; + +export const findByEPersonAndDsoRes = { + 'type': { + 'value': 'paginated-list' + }, + 'pageInfo': { + 'elementsPerPage': 20, + 'totalElements': 2, + 'totalPages': 1, + 'currentPage': 1 + }, + '_links': { + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/search/findByEPersonAndDso?dspace_object_id=092b59e8-8159-4e70-98b5-93ec60bd3431&eperson_id=335647b6-8a52-4ecb-a8c1-7ebabb199bda' + }, + 'page': [ + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22' + }, + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/48' + } + ] + }, + 'page': [ + { + 'id': 22, + 'subscriptionType': 'content', + 'subscriptionParameterList': [ + { + 'id': 161, + 'name': 'frequency', + 'value': 'M' + } + ], + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22' + } + }, + 'type': 'subscription' + }, + { + 'id': 48, + 'subscriptionType': 'statistics', + 'subscriptionParameterList': [ + { + 'id': 159, + 'name': 'frequency', + 'value': 'D' + }, + { + 'id': 160, + 'name': 'frequency', + 'value': 'M' + } + ], + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/48/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/48/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/48' + } + }, + 'type': 'subscription' + } + ] +}; + + +export const findByEPersonAndDsoResEmpty = { + 'type': { + 'value': 'paginated-list' + }, + 'pageInfo': { + 'elementsPerPage': 0, + 'totalElements': 0, + 'totalPages': 1, + 'currentPage': 1 + }, + '_links': { + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/search/findByEPersonAndDso?dspace_object_id=092b59e8-8159-4e70-98b5-93ec60bd3431&eperson_id=335647b6-8a52-4ecb-a8c1-7ebabb199bda' + }, + 'page': [ + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22' + }, + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/48' + } + ] + }, + 'page': [] +}; + +export const subscription = { + 'id': 21, + 'type': 'subscription', + 'subscriptionParameterList': [ + { + 'id': 77, + 'name': 'frequency', + 'value': 'D' + }, + { + 'id': 78, + 'name': 'frequency', + 'value': 'M' + } + ], + 'subscriptionType': 'content', + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21' + } + } +}; + + diff --git a/src/app/shared/testing/test-module.ts b/src/app/shared/testing/test-module.ts index 7d45dd41f3..85fa295dc2 100644 --- a/src/app/shared/testing/test-module.ts +++ b/src/app/shared/testing/test-module.ts @@ -25,9 +25,10 @@ import { BrowserOnlyMockPipe } from './browser-only-mock.pipe'; NgComponentOutletDirectiveStub, BrowserOnlyMockPipe, ], - exports: [ - QueryParamsDirectiveStub - ], + exports: [ + QueryParamsDirectiveStub, + RouterLinkDirectiveStub + ], schemas: [ CUSTOM_ELEMENTS_SCHEMA ] diff --git a/src/app/subscriptions-page/subscriptions-page-routing.module.ts b/src/app/subscriptions-page/subscriptions-page-routing.module.ts new file mode 100644 index 0000000000..149c9a415f --- /dev/null +++ b/src/app/subscriptions-page/subscriptions-page-routing.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { SubscriptionsPageModule } from './subscriptions-page.module'; +import { SubscriptionsPageComponent } from './subscriptions-page.component'; + + +@NgModule({ + imports: [ + SubscriptionsPageModule, + RouterModule.forChild([ + { + path: '', + data: { + title: 'subscriptions.title', + }, + children: [ + { + path: '', + component: SubscriptionsPageComponent, + }, + ] + }, + ]) + ] +}) +export class SubscriptionsPageRoutingModule { +} diff --git a/src/app/subscriptions-page/subscriptions-page.component.html b/src/app/subscriptions-page/subscriptions-page.component.html new file mode 100644 index 0000000000..bfbdbe72fc --- /dev/null +++ b/src/app/subscriptions-page/subscriptions-page.component.html @@ -0,0 +1,45 @@ +
+
+
+

{{'subscriptions.title' | translate}}

+
+
+ + + + +
+ + + + + + + + + + + + + +
{{'subscriptions.table.dso' | translate}}{{'subscriptions.table.subscription_type' | translate}}{{'subscriptions.table.subscription_frequency' | translate}}{{'subscriptions.table.action' | translate}}
+
+
+ +
+ {{ 'subscriptions.table.empty.message' | translate }} +
+ +
+
+ +
+
+
+
diff --git a/src/app/subscriptions-page/subscriptions-page.component.scss b/src/app/subscriptions-page/subscriptions-page.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/subscriptions-page/subscriptions-page.component.spec.ts b/src/app/subscriptions-page/subscriptions-page.component.spec.ts new file mode 100644 index 0000000000..77d2f6805a --- /dev/null +++ b/src/app/subscriptions-page/subscriptions-page.component.spec.ts @@ -0,0 +1,127 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; + + +// Import modules +import { CommonModule } from '@angular/common'; +import { BrowserModule, By } from '@angular/platform-browser'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; + + +// Import components +import { SubscriptionsPageComponent } from './subscriptions-page.component'; + +// Import services +import { PaginationService } from '../core/pagination/pagination.service'; +import { SubscriptionService } from '../shared/subscriptions/subscription.service'; +import { PaginationServiceStub } from '../shared/testing/pagination-service.stub'; +import { AuthService } from '../core/auth/auth.service'; + +// Import utils +import { HostWindowService } from '../shared/host-window.service'; +import { HostWindowServiceStub } from '../shared/testing/host-window-service.stub'; + + +// Import mocks +import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; +import { findAllSubscriptionRes } from '../shared/testing/subscriptions-data.mock'; +import { MockActivatedRoute } from '../shared/mocks/active-router.mock'; +import { of as observableOf } from 'rxjs'; +import { EPersonMock } from '../shared/testing/eperson.mock'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { VarDirective } from '../shared/utils/var.directive'; +import { + SubscriptionViewComponent +} from '../shared/subscriptions/components/subscription-view/subscription-view.component'; + + +describe('SubscriptionsPageComponent', () => { + let component: SubscriptionsPageComponent; + let fixture: ComponentFixture; + let de: DebugElement; + + const authServiceStub = jasmine.createSpyObj('authorizationService', { + getAuthenticatedUserFromStore: observableOf(EPersonMock) + }); + + const subscriptionServiceStub = jasmine.createSpyObj('SubscriptionService', { + findByEPerson: observableOf(findAllSubscriptionRes) + }); + const paginationService = new PaginationServiceStub(); + + beforeEach(waitForAsync( () => { + TestBed.configureTestingModule({ + imports: [ + CommonModule, + BrowserModule, + RouterTestingModule.withRoutes([]), + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + NoopAnimationsModule + ], + declarations: [ SubscriptionsPageComponent, SubscriptionViewComponent, VarDirective ], + providers:[ + { provide: SubscriptionService, useValue: subscriptionServiceStub }, + { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, + { provide: ActivatedRoute, useValue: new MockActivatedRoute() }, + { provide: AuthService, useValue: authServiceStub }, + { provide: PaginationService, useValue: paginationService } + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SubscriptionsPageComponent); + component = fixture.componentInstance; + de = fixture.debugElement; + fixture.detectChanges(); + }); + + it('should create', () => { + fixture.detectChanges(); + expect(component).toBeTruthy(); + }); + + describe('when table', () => { + + it('should show table', async() => { + await fixture.whenStable(); + fixture.detectChanges(); + const table = de.query(By.css('table')); + expect(table).toBeTruthy(); + }); + + }); + + it('should show all the results', () => { + expect(de.queryAll(By.css('tbody > tr')).length).toEqual(10); + }); + + it('should have dso object info', () => { + expect(de.query(By.css('.dso-info > span'))).toBeTruthy(); + expect(de.query(By.css('.dso-info > p > a'))).toBeTruthy(); + }); + + it('should have subscription type info', () => { + expect(de.query(By.css('.subscription-type'))).toBeTruthy(); + }); + + it('should have subscription paramenter info', () => { + expect(de.query(By.css('.subscription-parmenters > span'))).toBeTruthy(); + }); + + it('should have subscription action info', () => { + expect(de.query(By.css('.btn-outline-primary'))).toBeTruthy(); + expect(de.query(By.css('.btn-outline-danger'))).toBeTruthy(); + }); + + +}); diff --git a/src/app/subscriptions-page/subscriptions-page.component.ts b/src/app/subscriptions-page/subscriptions-page.component.ts new file mode 100644 index 0000000000..b81b7c5617 --- /dev/null +++ b/src/app/subscriptions-page/subscriptions-page.component.ts @@ -0,0 +1,117 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { BehaviorSubject, Subscription as rxSubscription } from 'rxjs'; +import { switchMap, take } from 'rxjs/operators'; +import { Subscription } from '../shared/subscriptions/models/subscription.model'; +import { buildPaginatedList, PaginatedList } from '../core/data/paginated-list.model'; +import { SubscriptionService } from '../shared/subscriptions/subscription.service'; +import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; +import { PaginationService } from '../core/pagination/pagination.service'; +import { PageInfo } from '../core/shared/page-info.model'; +import { AuthService } from '../core/auth/auth.service'; +import { EPerson } from '../core/eperson/models/eperson.model'; + +@Component({ + selector: 'ds-subscriptions-page', + templateUrl: './subscriptions-page.component.html', + styleUrls: ['./subscriptions-page.component.scss'] +}) +export class SubscriptionsPageComponent implements OnInit, OnDestroy { + + /** + * The subscriptions to show on this page, as an Observable list. + */ + subscriptions$: BehaviorSubject> = new BehaviorSubject(buildPaginatedList(new PageInfo(), [])); + + /** + * The current pagination configuration for the page used by the FindAll method + * Currently simply renders subscriptions + */ + config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { + id: 'elp', + pageSize: 10, + currentPage: 1 + }); + + /** + * Subscription to be unsubscribed + */ + sub: rxSubscription; + + /** + * A boolean representing if is loading + */ + loading$: BehaviorSubject = new BehaviorSubject(false); + + /** + * EPerson id of the logged in user + */ + eperson: string; + + constructor( + private paginationService: PaginationService, + private authService: AuthService, + private subscriptionService: SubscriptionService + ) { } + + /** + * Subscribe the pagination service to send a request with specific pagination + * When page is changed it will request the new subscriptions for the new page config + */ + ngOnInit(): void { + this.authService.getAuthenticatedUserFromStore().pipe(take(1)).subscribe( (eperson: EPerson) => { + this.eperson = eperson.id; + + this.sub = this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( + switchMap((findListOptions) => { + this.loading$.next(true); + return this.subscriptionService.findByEPerson(this.eperson,{ + currentPage: findListOptions.currentPage, + elementsPerPage: findListOptions.pageSize + }); + } + ) + ).subscribe({ + next: (res: any) => { + this.subscriptions$.next(res); + this.loading$.next(false); + }, + error: () => { + this.loading$.next(false); + } + }); + }); + } + + /** + * When an action is made and the information is changed refresh the information + */ + refresh(): void { + this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( + take(1), + switchMap((findListOptions) => { + this.loading$.next(true); + return this.subscriptionService.findByEPerson(this.eperson,{ + currentPage: findListOptions.currentPage, + elementsPerPage: findListOptions.pageSize + }); + } + ) + ).subscribe({ + next: (res: any) => { + this.subscriptions$.next(res); + this.loading$.next(false); + }, + error: () => { + this.loading$.next(false); + } + }); + } + + /** + * Unsubscribe from pagination subscription + */ + ngOnDestroy(): void { + this.sub.unsubscribe(); + } + +} diff --git a/src/app/subscriptions-page/subscriptions-page.module.ts b/src/app/subscriptions-page/subscriptions-page.module.ts new file mode 100644 index 0000000000..f7a4dc3344 --- /dev/null +++ b/src/app/subscriptions-page/subscriptions-page.module.ts @@ -0,0 +1,15 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SubscriptionsPageComponent } from './subscriptions-page.component'; +import { SharedModule } from '../shared/shared.module'; +import { SubscriptionsModule } from '../shared/subscriptions/subscriptions.module'; + +@NgModule({ + declarations: [SubscriptionsPageComponent], + imports: [ + CommonModule, + SharedModule, + SubscriptionsModule, + ] +}) +export class SubscriptionsPageModule { } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index d043d61ae5..12a21beaa2 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2298,6 +2298,22 @@ "item.page.claim.tooltip": "Claim this item as profile", + "item.page.subscriptions.tooltip": "Subscribe", + + "item.page.subscriptions.modal.title": "Subscriptions", + + "item.page.subscriptions.modal.close": "Close", + + "item.page.subscriptions.modal.new-subscription-form.type.content": "Content", + + "item.page.subscriptions.modal.new-subscription-form.frequency.D": "daily", + + "item.page.subscriptions.modal.new-subscription-form.frequency.M": "monthly", + + "item.page.subscriptions.modal.new-subscription-form.frequency.W": "weekly", + + "item.page.subscriptions.modal.new-subscription-form.submit": "Submit", + "item.preview.dc.identifier.uri": "Identifier:", "item.preview.dc.contributor.author": "Authors:", From f15ca85afd9043aedb4402d619d721ebc66f1ba3 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 23 Dec 2022 13:18:40 +0100 Subject: [PATCH 02/29] [CST-7757] Subscriptions porting (wip) --- .../subscription-modal.component.html | 5 +- .../subscription-modal.component.ts | 168 +++++------------- 2 files changed, 50 insertions(+), 123 deletions(-) diff --git a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.html b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.html index 53119d9a15..52746fb5c8 100644 --- a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.html +++ b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.html @@ -17,9 +17,8 @@
-
- +
+
diff --git a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts index c249cc592b..e0d025be54 100644 --- a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts +++ b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts @@ -1,38 +1,23 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; -import { - AbstractControl, - FormArray, - FormBuilder, - FormControl, - FormGroup, - ValidatorFn, - Validators -} from '@angular/forms'; +import { FormBuilder, FormGroup } from '@angular/forms'; import { Subscription } from '../../models/subscription.model'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { BehaviorSubject, Observable, shareReplay } from 'rxjs'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { SubscriptionService } from '../../subscription.service'; import { NotificationsService } from '../../../notifications/notifications.service'; -import { NotificationType } from '../../../notifications/models/notification-type'; -import { NotificationOptions } from '../../../notifications/models/notification-options.model'; -import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { PaginatedList } from '../../../../core/data/paginated-list.model'; -import { hasValue } from '../../../empty.util'; -import { ConfirmationModalComponent } from '../../../confirmation-modal/confirmation-modal.component'; - -import { filter, map, switchMap, take, tap } from 'rxjs/operators'; -import { NoContent } from '../../../../core/shared/NoContent.model'; +import { map, switchMap, take, tap } from 'rxjs/operators'; import { RemoteData } from '../../../../core/data/remote-data'; import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; -import { EPerson } from '../../../../core/eperson/models/eperson.model'; import { AuthService } from '../../../../core/auth/auth.service'; @Component({ @@ -70,6 +55,8 @@ export class SubscriptionModalComponent implements OnInit { ePersonId$: Observable; + ePersonId: string; + /** * Types of subscription to be shown on select */ @@ -98,6 +85,10 @@ export class SubscriptionModalComponent implements OnInit { this.ePersonId$ = this.authService.getAuthenticatedUserFromStore().pipe( take(1), map((ePerson) => ePerson.uuid), + tap((res) => { + this.ePersonId = res; + }), + shareReplay(), ); this.subscriptionForm = this.formBuilder.group({}); @@ -138,9 +129,9 @@ export class SubscriptionModalComponent implements OnInit { // TODO loop over subscription types // for (let type of this.subscriptionTypes) { - const type = 'content'; // remove + const type = 'content'; // TODO remove const subscription = this.subscriptions.find((s) => s.subscriptionType === type); - // TODO manage multiple subscriptions with same tipe (there should be only one) + // TODO manage multiple subscriptions with same type (there should be only one) for (let parameter of subscription.subscriptionParameterList.filter((p) => p.name === 'frequency')) { this.subscriptionForm.controls[parameter.value]?.setValue(true); } @@ -169,29 +160,45 @@ export class SubscriptionModalComponent implements OnInit { submit() { - // TODO + // for (let type of this.subscriptionTypes) { - /* - - remove subscription if no checkbox is selected - - add subscription if it does not exist - - edit subscription if it already exists - */ + const type = 'content'; // TODO remove - const body = { - type: 'content', - subscriptionParameterList: [] - }; + const currentSubscription = this.subscriptions?.find((s) => s.subscriptionType === type); - for (let frequency of this.frequencies) { - if (this.subscriptionForm.value[frequency]) { - body.subscriptionParameterList.push( - { - name: 'frequency', - value: frequency, - } - ); + const body = { + id: currentSubscription?.id, + type, + subscriptionParameterList: [] + }; + + let someCheckboxSelected = false; + + for (let frequency of this.frequencies) { + if (this.subscriptionForm.value[frequency]) { // TODO read the value for the type + someCheckboxSelected = true; + body.subscriptionParameterList.push( + { + name: 'frequency', + value: frequency, + } + ); + } } - } + + if (currentSubscription && someCheckboxSelected) { + console.log('UPDATE'); + this.subscriptionService.updateSubscription(body, this.ePersonId, this.dso.uuid).subscribe(console.log); + } else if (currentSubscription && !someCheckboxSelected) { + console.log('DELETE'); + this.subscriptionService.deleteSubscription(currentSubscription.id).subscribe(console.log); + } else if (someCheckboxSelected) { + console.log('CREATE'); + this.subscriptionService.createSubscription(body, this.ePersonId, this.dso.uuid).subscribe(console.log); + } + + + // } // this.subscriptionService.createSubscription(body, this.ePersonId, this.dso.uuid).subscribe((res) => { // // this.refresh(); @@ -206,43 +213,6 @@ export class SubscriptionModalComponent implements OnInit { } - /** - * Sends request to create a new subscription, refreshes the table of subscriptions and notifies about summary page - */ - /*createForm(body): void { - this.subscriptionService.createSubscription(body, this.ePersonId, this.dso.uuid).subscribe((res) => { - this.refresh(); - this.notify(); - this.processing$.next(false); - }, - err => { - this.processing$.next(false); - } - ); - }*/ - - /** - * Sends request to update a subscription, refreshes the table of subscriptions and notifies about summary page - */ - /*updateForm(body) { - this.subscriptionService.updateSubscription(body, this.ePersonId, this.dso.uuid).subscribe((res) => { - this.refresh(); - this.notify(); - this.processing$.next(false); - }, - err => { - this.processing$.next(false); - } - ); - }*/ - - - /** - * Sends the request to delete the subscription with a specific id - */ - /*deleteSubscription(id): Observable { - return this.subscriptionService.deleteSubscription(id); - }*/ /** * Creates a notification with the link to the subscription summary page @@ -261,46 +231,4 @@ export class SubscriptionModalComponent implements OnInit { ); }*/ - /** - * When an action is done it will reinitialize the table and remove subscription form - */ - /*refresh(): void { - this.initSubscription(); - this.subscriptionForm = null; - this.submitted = false; - }*/ - - /** - * Returns if a specific frequency exists in the subscriptionParameterList - */ - getIsChecked(frequency): boolean { - return !!this.subscriptionForm.get('subscriptionParameterList').value.find(el => el.value === frequency.value); - } - - /** - * Deletes Subscription, show notification on success/failure & updates list - * - * @param subscription Subscription to be deleted - */ - /*deleteSubscriptionPopup(subscription: Subscription): void { - if (hasValue(subscription.id)) { - const modalRef = this.modalService.open(ConfirmationModalComponent); - modalRef.componentInstance.dso = this.dso; - modalRef.componentInstance.headerLabel = 'confirmation-modal.delete-subscription.header'; - modalRef.componentInstance.infoLabel = 'confirmation-modal.delete-subscription.info'; - modalRef.componentInstance.cancelLabel = 'confirmation-modal.delete-subscription.cancel'; - modalRef.componentInstance.confirmLabel = 'confirmation-modal.delete-subscription.confirm'; - modalRef.componentInstance.brandColor = 'danger'; - modalRef.componentInstance.confirmIcon = 'fas fa-trash'; - - modalRef.componentInstance.response.pipe( - take(1), - filter((confirm: boolean) => confirm), - switchMap(() => this.deleteSubscription(subscription.id)) - ).subscribe(() => { - this.refresh(); - }); - - } - }*/ } From 3ab552ed389c1d2b660985c75e2d004202b56765 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 23 Dec 2022 14:06:28 +0100 Subject: [PATCH 03/29] [CST-7757] Subscriptions porting (wip) --- .../subscription-modal.component.ts | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts index e0d025be54..39cc79ff8d 100644 --- a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts +++ b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts @@ -17,8 +17,10 @@ import { PaginatedList } from '../../../../core/data/paginated-list.model'; import { map, switchMap, take, tap } from 'rxjs/operators'; import { RemoteData } from '../../../../core/data/remote-data'; -import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; +import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; import { AuthService } from '../../../../core/auth/auth.service'; +import { NotificationType } from '../../../notifications/models/notification-type'; +import { NotificationOptions } from '../../../notifications/models/notification-options.model'; @Component({ selector: 'ds-subscription-modal', @@ -188,7 +190,17 @@ export class SubscriptionModalComponent implements OnInit { if (currentSubscription && someCheckboxSelected) { console.log('UPDATE'); - this.subscriptionService.updateSubscription(body, this.ePersonId, this.dso.uuid).subscribe(console.log); + this.subscriptionService.updateSubscription(body, this.ePersonId, this.dso.uuid).pipe( + getFirstCompletedRemoteData(), + ).subscribe((res) => { + if (res.hasSucceeded) { + this.notifySuccess(); + this.activeModal.close(); + } else { + this.notifyFailure(); + } + } + ); } else if (currentSubscription && !someCheckboxSelected) { console.log('DELETE'); this.subscriptionService.deleteSubscription(currentSubscription.id).subscribe(console.log); @@ -217,7 +229,7 @@ export class SubscriptionModalComponent implements OnInit { /** * Creates a notification with the link to the subscription summary page */ - /*notify(): void { + notifySuccess(): void { const options = new NotificationOptions(); options.timeOut = 0; const link = '/subscriptions'; @@ -229,6 +241,11 @@ export class SubscriptionModalComponent implements OnInit { 'context-menu.actions.subscription.notification.content', 'here' ); - }*/ + } + + notifyFailure() { + console.error('error'); + // TODO + } } From a6cd39c9067b1ee4dd7c004a28519becc83e5467 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 23 Dec 2022 18:25:04 +0100 Subject: [PATCH 04/29] [CST-7757] Subscriptions porting (wip) --- .../subscription-modal.component.ts | 39 ++++++++--- .../subscriptions-page.component.html | 2 +- .../subscriptions-page.component.ts | 66 +++++++++++-------- src/assets/i18n/en.json5 | 32 +++++++++ 4 files changed, 101 insertions(+), 38 deletions(-) diff --git a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts index 39cc79ff8d..b283e12d34 100644 --- a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts +++ b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts @@ -188,25 +188,44 @@ export class SubscriptionModalComponent implements OnInit { } } - if (currentSubscription && someCheckboxSelected) { - console.log('UPDATE'); - this.subscriptionService.updateSubscription(body, this.ePersonId, this.dso.uuid).pipe( - getFirstCompletedRemoteData(), - ).subscribe((res) => { + if (currentSubscription) { + const subscriptionsToBeRemobed = this.subscriptions?.filter( + (s) => s.subscriptionType === type && s.id !== currentSubscription.id + ); + for (let s of subscriptionsToBeRemobed) { + this.subscriptionService.deleteSubscription(currentSubscription.id).pipe( + getFirstCompletedRemoteData(), + ).subscribe((res) => { if (res.hasSucceeded) { - this.notifySuccess(); - this.activeModal.close(); + console.warn(`An additional subscription with type=${type} and id=${s.id} has been removed`); } else { this.notifyFailure(); } + }); + } + } + + + if (currentSubscription && someCheckboxSelected) { + // Update the existing subscription + this.subscriptionService.updateSubscription(body, this.ePersonId, this.dso.uuid).pipe( + getFirstCompletedRemoteData(), + ).subscribe((res) => { + if (res.hasSucceeded) { + this.notifySuccess(); + this.activeModal.close(); + } else { + this.notifyFailure(); } - ); + }); } else if (currentSubscription && !someCheckboxSelected) { - console.log('DELETE'); + // Delete the existing subscription this.subscriptionService.deleteSubscription(currentSubscription.id).subscribe(console.log); + // TODO handle notifications } else if (someCheckboxSelected) { - console.log('CREATE'); + // Create a new subscription this.subscriptionService.createSubscription(body, this.ePersonId, this.dso.uuid).subscribe(console.log); + // TODO handle notifications } diff --git a/src/app/subscriptions-page/subscriptions-page.component.html b/src/app/subscriptions-page/subscriptions-page.component.html index bfbdbe72fc..9d1e6d504e 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.html +++ b/src/app/subscriptions-page/subscriptions-page.component.html @@ -10,7 +10,7 @@ diff --git a/src/app/subscriptions-page/subscriptions-page.component.ts b/src/app/subscriptions-page/subscriptions-page.component.ts index b81b7c5617..2951cf037c 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.ts +++ b/src/app/subscriptions-page/subscriptions-page.component.ts @@ -1,6 +1,6 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { BehaviorSubject, Subscription as rxSubscription } from 'rxjs'; -import { switchMap, take } from 'rxjs/operators'; +import { BehaviorSubject, combineLatestWith, Observable, of, shareReplay, Subscription as rxSubscription } from 'rxjs'; +import { combineLatest, map, switchMap, take, tap } from 'rxjs/operators'; import { Subscription } from '../shared/subscriptions/models/subscription.model'; import { buildPaginatedList, PaginatedList } from '../core/data/paginated-list.model'; import { SubscriptionService } from '../shared/subscriptions/subscription.service'; @@ -9,6 +9,7 @@ import { PaginationService } from '../core/pagination/pagination.service'; import { PageInfo } from '../core/shared/page-info.model'; import { AuthService } from '../core/auth/auth.service'; import { EPerson } from '../core/eperson/models/eperson.model'; +import { getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; @Component({ selector: 'ds-subscriptions-page', @@ -42,10 +43,12 @@ export class SubscriptionsPageComponent implements OnInit, OnDestroy { */ loading$: BehaviorSubject = new BehaviorSubject(false); + ePersonId$: Observable; + /** * EPerson id of the logged in user */ - eperson: string; + // ePersonId: string; constructor( private paginationService: PaginationService, @@ -58,27 +61,32 @@ export class SubscriptionsPageComponent implements OnInit, OnDestroy { * When page is changed it will request the new subscriptions for the new page config */ ngOnInit(): void { - this.authService.getAuthenticatedUserFromStore().pipe(take(1)).subscribe( (eperson: EPerson) => { - this.eperson = eperson.id; - - this.sub = this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( - switchMap((findListOptions) => { - this.loading$.next(true); - return this.subscriptionService.findByEPerson(this.eperson,{ - currentPage: findListOptions.currentPage, - elementsPerPage: findListOptions.pageSize - }); - } - ) - ).subscribe({ - next: (res: any) => { - this.subscriptions$.next(res); - this.loading$.next(false); - }, - error: () => { - this.loading$.next(false); - } - }); + this.ePersonId$ = this.authService.getAuthenticatedUserFromStore().pipe( + take(1), + map((ePerson: EPerson) => ePerson.id), + shareReplay(), + /*tap((ePersonId: string) => { // TODO unused + this.ePersonId = ePersonId; + }),*/ + ); + const currentPagination$ = this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( + tap(console.log), + combineLatestWith(this.ePersonId$), + tap(() => {this.loading$.next(true);}), + switchMap(([currentPagination, ePersonId]) => this.subscriptionService.findByEPerson(ePersonId,{ + currentPage: currentPagination.currentPage, + elementsPerPage: currentPagination.pageSize + })), + tap((x) => console.log('find', x)), + // getFirstSucceededRemoteDataPayload(), + ).subscribe({ + next: (res: any) => { + this.subscriptions$.next(res); + this.loading$.next(false); + }, + error: () => { + this.loading$.next(false); + } }); } @@ -86,11 +94,11 @@ export class SubscriptionsPageComponent implements OnInit, OnDestroy { * When an action is made and the information is changed refresh the information */ refresh(): void { - this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( + /*this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( take(1), switchMap((findListOptions) => { this.loading$.next(true); - return this.subscriptionService.findByEPerson(this.eperson,{ + return this.subscriptionService.findByEPerson(this.ePersonId,{ currentPage: findListOptions.currentPage, elementsPerPage: findListOptions.pageSize }); @@ -104,7 +112,7 @@ export class SubscriptionsPageComponent implements OnInit, OnDestroy { error: () => { this.loading$.next(false); } - }); + });*/ } /** @@ -114,4 +122,8 @@ export class SubscriptionsPageComponent implements OnInit, OnDestroy { this.sub.unsubscribe(); } + obs(v) { + return of(v); + } + } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 12a21beaa2..2af33eb11d 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2915,6 +2915,8 @@ "nav.stop-impersonating": "Stop impersonating EPerson", + "nav.subscriptions" : "Subscriptions", + "nav.toggle" : "Toggle navigation", "nav.user.description" : "User profile bar", @@ -4492,6 +4494,36 @@ "submission.workspace.generic.view-help": "Select this option to view the item's metadata.", + "subscriptions.title": "Subscriptions", + + "subscriptions.item": "Subscriptions for items", + + "subscriptions.collection": "Subscriptions for collections", + + "subscriptions.community": "Subscriptions for communities", + + "subscriptions.subscription_type": "Subscription type", + + "subscriptions.frequency": "Subscription frequency", + + "subscriptions.frequency.D": "Daily", + + "subscriptions.frequency.M": "Monthly", + + "subscriptions.frequency.W": "Weekly", + + "subscriptions.table.dso": "Subject", + + "subscriptions.table.subscription_type": "Subscription Type", + + "subscriptions.table.subscription_frequency": "Subscription Frequency", + + "subscriptions.table.action": "Action", + + "subscriptions.table.empty.message": "You have not subscribed any notification yet. To subscribe notification about an object please use the contextual menu in the object detail view", + + + "thumbnail.default.alt": "Thumbnail Image", "thumbnail.default.placeholder": "No Thumbnail Available", From 72a42b0a6e4df708e5bbe97e06033575e5fc5625 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 27 Dec 2022 20:10:47 +0100 Subject: [PATCH 05/29] [CST-7757] Refactoring code --- ...so-page-subscription-button.component.html | 2 +- ...page-subscription-button.component.spec.ts | 66 ++++- .../dso-page-subscription-button.component.ts | 7 +- .../subscription-edit-modal.component.html | 44 --- .../subscription-edit-modal.component.scss | 4 - .../subscription-edit-modal.component.spec.ts | 116 -------- .../subscription-edit-modal.component.ts | 190 ------------ .../subscription-modal.component.scss | 12 - .../subscription-modal.component.spec.ts | 223 --------------- .../subscription-modal.component.ts | 270 ------------------ .../models/subscription.model.ts | 30 +- .../subscription-modal.component.html | 15 +- .../subscription-modal.component.scss} | 0 .../subscription-modal.component.spec.ts | 194 +++++++++++++ .../subscription-modal.component.ts | 261 +++++++++++++++++ .../subscription-view.component.html | 7 +- .../subscription-view.component.scss | 0 .../subscription-view.component.spec.ts | 37 ++- .../subscription-view.component.ts | 42 +-- .../subscriptions/subscription.service.ts | 11 +- .../subscriptions/subscriptions.module.ts | 17 +- .../shared/testing/subscriptions-data.mock.ts | 33 ++- .../subscriptions-page.component.html | 13 +- .../subscriptions-page.component.ts | 55 +--- src/assets/i18n/en.json5 | 21 ++ 25 files changed, 689 insertions(+), 981 deletions(-) delete mode 100644 src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.html delete mode 100644 src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.scss delete mode 100644 src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.spec.ts delete mode 100644 src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.ts delete mode 100644 src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.scss delete mode 100644 src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.spec.ts delete mode 100644 src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts rename src/app/shared/subscriptions/{components => }/subscription-modal/subscription-modal.component.html (64%) rename src/app/shared/subscriptions/{components/subscription-view/subscription-view.component.scss => subscription-modal/subscription-modal.component.scss} (100%) create mode 100644 src/app/shared/subscriptions/subscription-modal/subscription-modal.component.spec.ts create mode 100644 src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts rename src/app/shared/subscriptions/{components => }/subscription-view/subscription-view.component.html (69%) create mode 100644 src/app/shared/subscriptions/subscription-view/subscription-view.component.scss rename src/app/shared/subscriptions/{components => }/subscription-view/subscription-view.component.spec.ts (77%) rename src/app/shared/subscriptions/{components => }/subscription-view/subscription-view.component.ts (66%) diff --git a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html index 9956163944..1932c37000 100644 --- a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html +++ b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html @@ -1,4 +1,4 @@ - -
- - diff --git a/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.scss b/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.scss deleted file mode 100644 index fb9db5b0ff..0000000000 --- a/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.scss +++ /dev/null @@ -1,4 +0,0 @@ -.alert{ - font-weight: bold; - color:red; -} diff --git a/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.spec.ts b/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.spec.ts deleted file mode 100644 index d406fa2c1a..0000000000 --- a/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { ComponentFixture, ComponentFixtureAutoDetect, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing'; -import { cold } from 'jasmine-marbles'; - -// Import modules -import { CommonModule } from '@angular/common'; -import { ReactiveFormsModule } from '@angular/forms'; -import { By } from '@angular/platform-browser'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { DebugElement } from '@angular/core'; - -import { SubscriptionEditModalComponent } from './subscription-edit-modal.component'; - -// Import mocks -import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock'; -import { subscription } from '../../../testing/subscriptions-data.mock'; -import { ItemInfo } from '../../../testing/relationships-mocks'; - -// Import utils -import { NotificationsService } from '../../../notifications/notifications.service'; -import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; -import { SubscriptionService } from '../../subscription.service'; -import { Subscription } from '../../models/subscription.model'; - - -describe('SubscriptionEditModalComponent', () => { - let component: SubscriptionEditModalComponent; - let fixture: ComponentFixture; - let de: DebugElement; - - const subscriptionServiceStub = jasmine.createSpyObj('SubscriptionService', { - updateSubscription: jasmine.createSpy('updateSubscription'), - }); - - - beforeEach(waitForAsync (() => { - TestBed.configureTestingModule({ - imports: [ - CommonModule, - ReactiveFormsModule, - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock - } - }), - ], - declarations: [ SubscriptionEditModalComponent ], - providers: [ - { provide: ComponentFixtureAutoDetect, useValue: true }, - { provide: NotificationsService, useValue: NotificationsServiceStub }, - { provide: SubscriptionService, useValue: subscriptionServiceStub }, - ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(SubscriptionEditModalComponent); - component = fixture.componentInstance; - component.eperson = 'testid123'; - component.dso = ItemInfo.payload; - - de = fixture.debugElement; - - subscriptionServiceStub.updateSubscription.and.returnValue(cold('a|', { - a: {} - })); - - - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('No Subscription inserted', () => { - it('should not show form', () => { - expect(de.query(By.css('form'))).toBeNull(); - }); - }); - - - describe('Subscription inserted', () => { - - beforeEach(fakeAsync(() => { - component.subscription = Object.assign(new Subscription(), subscription); - component.ngOnInit(); - fixture.detectChanges(); - })); - - it('when insert subscription show form', () => { - expect(de.query(By.css('form'))).toBeTruthy(); - }); - - it('should have right checkboxes checked', () => { - expect(de.query(By.css('#checkbox-0'))?.nativeElement?.checked).toEqual(true); - expect(de.query(By.css('#checkbox-1'))?.nativeElement?.checked).toEqual(true); - expect(de.query(By.css('#checkbox-2'))?.nativeElement?.checked).toEqual(false); - }); - - it('on checkbox clicked should change form values', () => { - const checkbox = de.query(By.css('#checkbox-2')).nativeElement; - checkbox.click(); - - expect(de.query(By.css('#checkbox-2'))?.nativeElement?.checked).toEqual(true); - expect(component.subscriptionParameterList?.value?.length).toEqual(3); - }); - - it('on submit clicked update should have been called', () => { - const button = de.query(By.css('.btn-success')).nativeElement; - button.click(); - expect(subscriptionServiceStub.updateSubscription).toHaveBeenCalled(); - }); - }); -}); diff --git a/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.ts b/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.ts deleted file mode 100644 index 46c895dc18..0000000000 --- a/src/app/shared/subscriptions/components/subscription-edit-modal/subscription-edit-modal.component.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; - -import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; - -import { Subscription } from '../../models/subscription.model'; - -import { BehaviorSubject } from 'rxjs'; - -import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; - -import { SubscriptionService } from '../../subscription.service'; -import { NotificationsService } from '../../../notifications/notifications.service'; - -import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; - -@Component({ - selector: 'ds-subscription-edit-modal', - templateUrl: './subscription-edit-modal.component.html', - styleUrls: ['./subscription-edit-modal.component.scss'] -}) -export class SubscriptionEditModalComponent implements OnInit { - - - /** - * DSpaceObject of the subscription - */ - @Input() dso: DSpaceObject; - - /** - * EPerson of the subscription - */ - @Input() eperson: string; - - /** - * List of subscription for the dso object and eperson relation - */ - @Input() subscription!: Subscription; - - /** - * Close event emit to close modal - */ - @Output() close: EventEmitter = new EventEmitter(); - - /** - * Reload event emit to refresh informations - */ - @Output() reload: EventEmitter = new EventEmitter(); - - /** - * A boolean representing if a request operation is pending - * @type {BehaviorSubject} - */ - public processing$ = new BehaviorSubject(false); - - /** - * Reactive form group that will be used to add subscriptions - */ - subscriptionForm: FormGroup; - - /** - * Used to show validation errors when user submits - */ - submitted = false; - - /** - * Reference to NgbModal - */ - public modalRef: NgbModalRef; - - - /** - * Frequencies to be shown as checkboxes - */ - frequencies = [ - {name: 'daily' ,value: 'D'}, - {name: 'monthly' ,value: 'M'}, - {name: 'weekly' ,value: 'W'}, - ]; - - constructor(private formGroup: FormBuilder, - private notificationsService: NotificationsService, - private subscriptionService: SubscriptionService - ) {} - - /** - * When component starts initialize starting functionality - */ - ngOnInit(): void { - this.initSubscription(); - } - - /** - * If the subscription is passed start the form with the information of subscription - */ - initSubscription(): void { - if (!!this.subscription) { - this.buildFormBuilder(this.subscription); - } - } - - /** - * Function to get subscriptionParameterList form array cleaner - */ - get subscriptionParameterList(): FormArray { - return this.subscriptionForm.get('subscriptionParameterList') as FormArray; - } - - /** - * When frequency checkboxes are being changed we add/remove frequencies from subscriptionParameterList - */ - selectCheckbox(event,frequency): void { - if (event.target.checked) { - this.addFrequency(frequency); - } else { - this.removeFrequency(frequency); - } - } - - /** - * Start the form with preinserted informations - */ - buildFormBuilder(subscription): void { - - this.subscriptionForm = this.formGroup.group({ - id: subscription.id, - type: subscription.subscriptionType, - subscriptionParameterList: this.formGroup.array([], Validators.required) - }); - - subscription.subscriptionParameterList.forEach( (parameter) => { - this.addFrequency(parameter.value); - }); - } - - /** - * Add a new frequency to the subscriptionParameterList form array - */ - addFrequency(frequency): void { - this.subscriptionParameterList.push( - this.formGroup.group({ - name: 'frequency', - value: frequency - }) - ); - } - - /** - * Remove frequency from subscriptionParameterList form array - */ - removeFrequency(frequency): void { - const index = this.subscriptionParameterList.controls.findIndex(el => el.value.value === frequency); - this.subscriptionParameterList.removeAt(index); - } - - /** - * When user saves it will check if form is valid and send request to update subscription - */ - submit(): void { - this.submitted = true; - if (this.subscriptionForm.valid) { - if (this.subscriptionForm.value.id) { - this.updateForm(this.subscriptionForm.value); - } - } - } - - /** - * Sends request to update a new subscription, refreshes the table of subscriptions and notifies about summary page - */ - updateForm(body): void { - this.subscriptionService.updateSubscription(body,this.eperson,this.dso.uuid).subscribe( (res) => { - this.reload.emit(); - this.close.emit(); - }); - } - - /** - * When close button is pressed emit function to close modal - */ - c(text): void { - this.close.emit(text); - } - - /** - * Returns if a specific frequency exists in the subscriptionParameterList - */ - getIsChecked(frequency): boolean { - return !!this.subscriptionForm.get('subscriptionParameterList').value.find(el => el.value === frequency.value); - } -} diff --git a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.scss b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.scss deleted file mode 100644 index 62dd1105e7..0000000000 --- a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.scss +++ /dev/null @@ -1,12 +0,0 @@ -.alert{ - font-weight: bold; - color:red; -} - -// .modal-footer{ -// justify-content: space-between; -// } - -.add-button{ - padding: 0px 15px 15px 15px; -} \ No newline at end of file diff --git a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.spec.ts b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.spec.ts deleted file mode 100644 index 088ec3004c..0000000000 --- a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.spec.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { ComponentFixture, ComponentFixtureAutoDetect, TestBed } from '@angular/core/testing'; - -import { of as observableOf } from 'rxjs'; - -// Import modules -import { CommonModule } from '@angular/common'; -import { ReactiveFormsModule } from '@angular/forms'; -import { By } from '@angular/platform-browser'; -import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { DebugElement } from '@angular/core'; - -import { SubscriptionModalComponent } from './subscription-modal.component'; - -// Import mocks -import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock'; -import { findByEPersonAndDsoRes, findByEPersonAndDsoResEmpty } from '../../../testing/subscriptions-data.mock'; -import { ItemInfo } from '../../../testing/relationships-mocks'; - -// Import utils -import { NotificationsService } from '../../../notifications/notifications.service'; -import { SubscriptionService } from '../../subscription.service'; - -import { createSuccessfulRemoteDataObject$ } from '../../../remote-data.utils'; - - -describe('SubscriptionModalComponent', () => { - let component: SubscriptionModalComponent; - let fixture: ComponentFixture; - let de: DebugElement; - - let subscriptionServiceStub; - const notificationServiceStub = { - notificationWithAnchor() { - return true; - } - }; - - - describe('when empty subscriptions', () => { - - beforeEach(async () => { - - subscriptionServiceStub = jasmine.createSpyObj('SubscriptionService', { - getSubscriptionByPersonDSO: observableOf(findByEPersonAndDsoResEmpty), - createSubscription: createSuccessfulRemoteDataObject$({}), - updateSubscription: createSuccessfulRemoteDataObject$({}), - }); - - await TestBed.configureTestingModule({ - imports: [ - CommonModule, - NgbModule, - ReactiveFormsModule, - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock - } - }), - ], - declarations: [SubscriptionModalComponent], - providers: [ - { provide: ComponentFixtureAutoDetect, useValue: true }, - { provide: NotificationsService, useValue: notificationServiceStub }, - { provide: SubscriptionService, useValue: subscriptionServiceStub }, - ] - }) - .compileComponents(); - - fixture = TestBed.createComponent(SubscriptionModalComponent); - component = fixture.componentInstance; - component.ePersonId = 'testid123'; - component.dso = ItemInfo.payload; - de = fixture.debugElement; - - await fixture.whenStable(); - await fixture.whenRenderingDone(); - - fixture.detectChanges(); - - }); - - it('should be no table', () => { - expect(de.query(By.css('table'))).toBeNull(); - }); - - it('should show empty form', () => { - expect(de.query(By.css('form'))).toBeTruthy(); - }); - - it('should show form with empty checkboxes', () => { - expect(de.query(By.css('#checkbox-0'))?.nativeElement?.checked).toEqual(false); - expect(de.query(By.css('#checkbox-1'))?.nativeElement?.checked).toEqual(false); - expect(de.query(By.css('#checkbox-2'))?.nativeElement?.checked).toEqual(false); - }); - - }); - - - describe('when we have subscriptions', () => { - - beforeEach(async () => { - - subscriptionServiceStub = jasmine.createSpyObj('SubscriptionService', { - getSubscriptionByPersonDSO: observableOf(findByEPersonAndDsoRes), - createSubscription: createSuccessfulRemoteDataObject$({}), - updateSubscription: createSuccessfulRemoteDataObject$({}), - }); - - await TestBed.configureTestingModule({ - imports: [ - CommonModule, - NgbModule, - ReactiveFormsModule, - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock - } - }), - ], - declarations: [SubscriptionModalComponent], - providers: [ - { provide: ComponentFixtureAutoDetect, useValue: true }, - { provide: NotificationsService, useValue: notificationServiceStub }, - { provide: SubscriptionService, useValue: subscriptionServiceStub }, - ] - }) - .compileComponents(); - - fixture = TestBed.createComponent(SubscriptionModalComponent); - component = fixture.componentInstance; - component.ePersonId = 'testid123'; - component.dso = ItemInfo.payload; - de = fixture.debugElement; - await fixture.whenStable(); - await fixture.whenRenderingDone(); - - fixture.detectChanges(); - - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should render 2 subscriptions', () => { - expect(de.queryAll(By.css('tbody > tr')).length).toEqual(2); - }); - - it('should show no form', () => { - expect(de.query(By.css('form'))).toBeNull(); - }); - - it('should have 2 edit buttons', () => { - expect(de.queryAll(By.css('.btn-outline-primary')).length).toEqual(2); - }); - - it('should have 2 delete buttons', () => { - expect(de.queryAll(By.css('.btn-outline-danger')).length).toEqual(2); - }); - - describe('When creating new subscription', () => { - - beforeEach(() => { - // add button click - const button = de.query(By.css('.btn-success')).nativeElement; - button.click(); - }); - - - it('should show form when add button click event', () => { - expect(de.query(By.css('form'))).toBeTruthy(); - }); - - it('should show form with empty checkboxes', () => { - expect(de.query(By.css('#checkbox-0'))?.nativeElement?.checked).toEqual(false); - expect(de.query(By.css('#checkbox-1'))?.nativeElement?.checked).toEqual(false); - expect(de.query(By.css('#checkbox-2'))?.nativeElement?.checked).toEqual(false); - }); - - it('should call create request when submit click event', () => { - const checkbox = de.query(By.css('#checkbox-2')).nativeElement; - checkbox.click(); - - const button = de.queryAll(By.css('.btn-success'))[1].nativeElement; - button.click(); - expect(subscriptionServiceStub.createSubscription).toHaveBeenCalled(); - }); - - }); - - - describe('When updating subscription', () => { - - beforeEach(() => { - // edit button click - const button = de.query(By.css('.btn-outline-primary')).nativeElement; - button.click(); - }); - - it('should show form when edit button click event', () => { - expect(de.query(By.css('form'))).toBeTruthy(); - }); - - it('should show form with empty checkboxes', () => { - expect(de.query(By.css('#checkbox-0'))?.nativeElement?.checked).toEqual(false); - expect(de.query(By.css('#checkbox-1'))?.nativeElement?.checked).toEqual(true); - expect(de.query(By.css('#checkbox-2'))?.nativeElement?.checked).toEqual(false); - }); - - it('should call update request when submit click event', () => { - const button = de.queryAll(By.css('.btn-success'))[1].nativeElement; - button.click(); - expect(subscriptionServiceStub.updateSubscription).toHaveBeenCalled(); - }); - - }); - - }); - -}); diff --git a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts b/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts deleted file mode 100644 index b283e12d34..0000000000 --- a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.ts +++ /dev/null @@ -1,270 +0,0 @@ -import { Component, Input, OnInit } from '@angular/core'; - -import { FormBuilder, FormGroup } from '@angular/forms'; - -import { Subscription } from '../../models/subscription.model'; - -import { BehaviorSubject, Observable, shareReplay } from 'rxjs'; - -import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; - -import { SubscriptionService } from '../../subscription.service'; -import { NotificationsService } from '../../../notifications/notifications.service'; - -import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; - -import { PaginatedList } from '../../../../core/data/paginated-list.model'; - -import { map, switchMap, take, tap } from 'rxjs/operators'; -import { RemoteData } from '../../../../core/data/remote-data'; -import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; -import { AuthService } from '../../../../core/auth/auth.service'; -import { NotificationType } from '../../../notifications/models/notification-type'; -import { NotificationOptions } from '../../../notifications/models/notification-options.model'; - -@Component({ - selector: 'ds-subscription-modal', - templateUrl: './subscription-modal.component.html', - styleUrls: ['./subscription-modal.component.scss'] -}) -export class SubscriptionModalComponent implements OnInit { - - /** - * DSpaceObject of which to get the subscriptions - */ - @Input() dso: DSpaceObject; - - /** - * List of subscription for the dso object and eperson relation - */ - subscriptions: Subscription[]; - - /** - * A boolean representing if a request operation is pending - * @type {BehaviorSubject} - */ - public processing$ = new BehaviorSubject(false); - - /** - * Reactive form group that will be used to add subscriptions - */ - subscriptionForm: FormGroup; - - /** - * Used to show validation errors when user submits - */ - submitted = false; - - ePersonId$: Observable; - - ePersonId: string; - - /** - * Types of subscription to be shown on select - */ - subscriptionTypes = [ 'content', 'statistics' ]; - - /** - * Frequencies to be shown as checkboxes - */ - frequencies = [ 'D', 'M', 'W' ]; - - constructor( - private formBuilder: FormBuilder, - private modalService: NgbModal, - private notificationsService: NotificationsService, - private subscriptionService: SubscriptionService, - public activeModal: NgbActiveModal, - private authService: AuthService, - ) { - } - - /** - * When component starts initialize starting functionality - */ - ngOnInit(): void { - - this.ePersonId$ = this.authService.getAuthenticatedUserFromStore().pipe( - take(1), - map((ePerson) => ePerson.uuid), - tap((res) => { - this.ePersonId = res; - }), - shareReplay(), - ); - - this.subscriptionForm = this.formBuilder.group({}); - - for (let f of this.frequencies) { - this.subscriptionForm.addControl(f, this.formBuilder.control(false)); - } - - // TODO iterate over subscription types - - /*this.subscriptionForm = this.formBuilder.group({}); - for (let t of this.subscriptionTypes) { - this.subscriptionForm.addControl(t, this.formBuilder.group({})); - for (let f of this.frequencies) { - this.subscriptionForm[t].addControl(f, this.formBuilder.control(false)); - } - }*/ - - - this.initSubscription(); - - } - - /** - * Get subscription for the eperson & dso object relation - * If no subscription start with an empty form - */ - initSubscription(): void { - this.processing$.next(true); - this.ePersonId$.pipe( - tap(console.log), - switchMap((ePersonId: string) => this.getSubscription(ePersonId, this.dso?.uuid)), - getFirstSucceededRemoteDataPayload(), - ).subscribe({ - next: (res: PaginatedList) => { - if (res.pageInfo.totalElements > 0) { - this.subscriptions = res.page; - - // TODO loop over subscription types - // for (let type of this.subscriptionTypes) { - const type = 'content'; // TODO remove - const subscription = this.subscriptions.find((s) => s.subscriptionType === type); - // TODO manage multiple subscriptions with same type (there should be only one) - for (let parameter of subscription.subscriptionParameterList.filter((p) => p.name === 'frequency')) { - this.subscriptionForm.controls[parameter.value]?.setValue(true); - } - // } - - } - this.processing$.next(false); - }, - error: err => { - this.processing$.next(false); - } - }); - } - - /** - * Function to get subscriptions based on the eperson & dso - * - * @param ePersonId Eperson that is logged in - * @param uuid DSpaceObject id that subscriptions are related to - */ - getSubscription(ePersonId: string, uuid: string): Observable>> { - return this.subscriptionService.getSubscriptionByPersonDSO(ePersonId, uuid); - } - - - - submit() { - - // for (let type of this.subscriptionTypes) { - - const type = 'content'; // TODO remove - - const currentSubscription = this.subscriptions?.find((s) => s.subscriptionType === type); - - const body = { - id: currentSubscription?.id, - type, - subscriptionParameterList: [] - }; - - let someCheckboxSelected = false; - - for (let frequency of this.frequencies) { - if (this.subscriptionForm.value[frequency]) { // TODO read the value for the type - someCheckboxSelected = true; - body.subscriptionParameterList.push( - { - name: 'frequency', - value: frequency, - } - ); - } - } - - if (currentSubscription) { - const subscriptionsToBeRemobed = this.subscriptions?.filter( - (s) => s.subscriptionType === type && s.id !== currentSubscription.id - ); - for (let s of subscriptionsToBeRemobed) { - this.subscriptionService.deleteSubscription(currentSubscription.id).pipe( - getFirstCompletedRemoteData(), - ).subscribe((res) => { - if (res.hasSucceeded) { - console.warn(`An additional subscription with type=${type} and id=${s.id} has been removed`); - } else { - this.notifyFailure(); - } - }); - } - } - - - if (currentSubscription && someCheckboxSelected) { - // Update the existing subscription - this.subscriptionService.updateSubscription(body, this.ePersonId, this.dso.uuid).pipe( - getFirstCompletedRemoteData(), - ).subscribe((res) => { - if (res.hasSucceeded) { - this.notifySuccess(); - this.activeModal.close(); - } else { - this.notifyFailure(); - } - }); - } else if (currentSubscription && !someCheckboxSelected) { - // Delete the existing subscription - this.subscriptionService.deleteSubscription(currentSubscription.id).subscribe(console.log); - // TODO handle notifications - } else if (someCheckboxSelected) { - // Create a new subscription - this.subscriptionService.createSubscription(body, this.ePersonId, this.dso.uuid).subscribe(console.log); - // TODO handle notifications - } - - - // } - - // this.subscriptionService.createSubscription(body, this.ePersonId, this.dso.uuid).subscribe((res) => { - // // this.refresh(); - // // this.notify(); - // // this.processing$.next(false); - // }, - // err => { - // // this.processing$.next(false); - // } - // ); - - } - - - - /** - * Creates a notification with the link to the subscription summary page - */ - notifySuccess(): void { - const options = new NotificationOptions(); - options.timeOut = 0; - const link = '/subscriptions'; - this.notificationsService.notificationWithAnchor( - NotificationType.Success, - options, - link, - 'context-menu.actions.subscription.notification.here-text', - 'context-menu.actions.subscription.notification.content', - 'here' - ); - } - - notifyFailure() { - console.error('error'); - // TODO - } - -} diff --git a/src/app/shared/subscriptions/models/subscription.model.ts b/src/app/shared/subscriptions/models/subscription.model.ts index b5d6977d39..36e43ce996 100644 --- a/src/app/shared/subscriptions/models/subscription.model.ts +++ b/src/app/shared/subscriptions/models/subscription.model.ts @@ -1,11 +1,14 @@ +import { Observable } from 'rxjs'; import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; -import { typedObject } from '../../../core/cache/builders/build-decorators'; +import { link, typedObject } from '../../../core/cache/builders/build-decorators'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { HALLink } from '../../../core/shared/hal-link.model'; import { SUBSCRIPTION } from './subscription.resource-type'; import { EPerson } from '../../../core/eperson/models/eperson.model'; - +import { RemoteData } from '../../../core/data/remote-data'; +import { EPERSON } from '../../../core/eperson/models/eperson.resource-type'; +import { DSPACE_OBJECT } from '../../../core/shared/dspace-object.resource-type'; @typedObject @inheritSerialization(DSpaceObject) @@ -40,14 +43,27 @@ export class Subscription extends DSpaceObject { dSpaceObject: HALLink; }; + /** + * The logo for this Community + * Will be undefined unless the logo {@link HALLink} has been resolved. + */ + @link(EPERSON) + ePerson?: Observable>; + + /** + * The logo for this Community + * Will be undefined unless the logo {@link HALLink} has been resolved. + */ + @link(DSPACE_OBJECT) + dSpaceObject?: Observable>; /** * The embedded ePerson & dSpaceObject for this Subscription */ - @deserialize - _embedded: { - ePerson: EPerson; - dSpaceObject: DSpaceObject; - }; + /* @deserialize + _embedded: { + ePerson: EPerson; + dSpaceObject: DSpaceObject; + };*/ } export interface SubscriptionParameterList { diff --git a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.html b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html similarity index 64% rename from src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.html rename to src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html index 52746fb5c8..ef9e26032d 100644 --- a/src/app/shared/subscriptions/components/subscription-modal/subscription-modal.component.html +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html @@ -1,4 +1,4 @@ -
+ - - - - - diff --git a/src/app/shared/subscriptions/subscription-view/subscription-view.component.scss b/src/app/shared/subscriptions/subscription-view/subscription-view.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.spec.ts b/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts similarity index 77% rename from src/app/shared/subscriptions/components/subscription-view/subscription-view.component.spec.ts rename to src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts index e32112c5a0..3ec826d6c5 100644 --- a/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.spec.ts +++ b/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts @@ -6,27 +6,27 @@ import { ReactiveFormsModule } from '@angular/forms'; import { BrowserModule, By } from '@angular/platform-browser'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { SharedModule } from '../../../shared.module'; +import { SharedModule } from '../../shared.module'; import { DebugElement } from '@angular/core'; import { RouterTestingModule } from '@angular/router/testing'; import { SubscriptionViewComponent } from './subscription-view.component'; // Import mocks -import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock'; -import { ItemInfo } from '../../../testing/relationships-mocks'; -import { findByEPersonAndDsoResEmpty, subscription } from '../../../testing/subscriptions-data.mock'; +import { TranslateLoaderMock } from '../../mocks/translate-loader.mock'; +import { findByEPersonAndDsoResEmpty, subscriptionMock } from '../../testing/subscriptions-data.mock'; // Import utils -import { NotificationsService } from '../../../notifications/notifications.service'; -import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; -import { SubscriptionService } from '../../subscription.service'; -import { Subscription } from '../../models/subscription.model'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; +import { SubscriptionService } from '../subscription.service'; +import { Subscription } from '../models/subscription.model'; import { of as observableOf } from 'rxjs'; -import { createSuccessfulRemoteDataObject$ } from '../../../remote-data.utils'; - +import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; +import { Item } from '../../../core/shared/item.model'; +import { ITEM } from '../../../core/shared/item.resource-type'; describe('SubscriptionViewComponent', () => { let component: SubscriptionViewComponent; @@ -40,6 +40,19 @@ describe('SubscriptionViewComponent', () => { updateSubscription: createSuccessfulRemoteDataObject$({}), }); + const mockItem = Object.assign(new Item(), { + id: 'fake-id', + uuid: 'fake-id', + handle: 'fake/handle', + lastModified: '2018', + type: ITEM, + _links: { + self: { + href: 'https://localhost:8000/items/fake-id' + } + } + }); + beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ @@ -70,8 +83,8 @@ describe('SubscriptionViewComponent', () => { fixture = TestBed.createComponent(SubscriptionViewComponent); component = fixture.componentInstance; component.eperson = 'testid123'; - component.dso = ItemInfo.payload; - component.subscription = Object.assign(new Subscription(), subscription); + component.dso = mockItem; + component.subscription = Object.assign(new Subscription(), subscriptionMock); de = fixture.debugElement; fixture.detectChanges(); }); diff --git a/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.ts b/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts similarity index 66% rename from src/app/shared/subscriptions/components/subscription-view/subscription-view.component.ts rename to src/app/shared/subscriptions/subscription-view/subscription-view.component.ts index cb831534b6..4844be5b79 100644 --- a/src/app/shared/subscriptions/components/subscription-view/subscription-view.component.ts +++ b/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts @@ -1,13 +1,17 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { Subscription } from '../../models/subscription.model'; -import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; +import { Subscription } from '../models/subscription.model'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { take } from 'rxjs/operators'; - import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { hasValue } from '../../../empty.util'; -import { ConfirmationModalComponent } from '../../../confirmation-modal/confirmation-modal.component'; -import { SubscriptionService } from '../../subscription.service'; + +import { hasValue } from '../../empty.util'; +import { ConfirmationModalComponent } from '../../confirmation-modal/confirmation-modal.component'; +import { SubscriptionService } from '../subscription.service'; +import { getCommunityModuleRoute } from '../../../community-page/community-page-routing-paths'; +import { getCollectionModuleRoute } from '../../../collection-page/collection-page-routing-paths'; +import { getItemModuleRoute } from '../../../item-page/item-page-routing-paths'; +import { SubscriptionModalComponent } from '../subscription-modal/subscription-modal.component'; @Component({ // eslint-disable-next-line @angular-eslint/component-selector @@ -47,16 +51,6 @@ export class SubscriptionViewComponent { private subscriptionService: SubscriptionService, ) { } - - /** - * Open modal - * - * @param content - */ - public openSubscription(content: any) { - this.modalRef = this.modalService.open(content); - } - /** * Return the prefix of the route to the dso object page ( e.g. "items") */ @@ -64,13 +58,13 @@ export class SubscriptionViewComponent { let routePrefix; switch (this.dso.type.toString()) { case 'community': - routePrefix = '/communities'; + routePrefix = getCommunityModuleRoute(); break; case 'collection': - routePrefix = '/collections'; + routePrefix = getCollectionModuleRoute(); break; case 'item': - routePrefix = '/items'; + routePrefix = getItemModuleRoute(); break; } return routePrefix; @@ -99,4 +93,14 @@ export class SubscriptionViewComponent { }); } } + + public openSubscriptionModal() { + this.modalRef = this.modalService.open(SubscriptionModalComponent); + this.modalRef.componentInstance.dso = this.dso; + this.modalRef.componentInstance.subscription = this.subscription; + this.modalRef.componentInstance.updateSubscription.pipe(take(1)).subscribe((subscription: Subscription) => { + this.subscription = subscription; + }) + + } } diff --git a/src/app/shared/subscriptions/subscription.service.ts b/src/app/shared/subscriptions/subscription.service.ts index 5010286e72..480598a15a 100644 --- a/src/app/shared/subscriptions/subscription.service.ts +++ b/src/app/shared/subscriptions/subscription.service.ts @@ -70,7 +70,7 @@ export class SubscriptionService extends IdentifiableDataService { * @param eperson The eperson to search for * @param uuid The uuid of the dsobjcet to search for */ - getSubscriptionByPersonDSO(eperson: string, uuid: string): Observable>> { + getSubscriptionsByPersonDSO(eperson: string, uuid: string): Observable>> { const optionsWithObject = Object.assign(new FindListOptions(), { searchParams: [ @@ -160,7 +160,14 @@ export class SubscriptionService extends IdentifiableDataService { ] }); - return this.searchData.searchBy(this.findByEpersonLinkPath, optionsWithObject, true, true, followLink('dSpaceObject'), followLink('ePerson')); + // return this.searchData.searchBy(this.findByEpersonLinkPath, optionsWithObject, true, true, followLink('dSpaceObject'), followLink('ePerson')); + + return this.getEndpoint().pipe( + map(href => `${href}/search/${this.findByEpersonLinkPath}`), + switchMap(href => this.findListByHref(href, optionsWithObject, true, true, followLink('dSpaceObject'), followLink('ePerson'))) + ); + + } } diff --git a/src/app/shared/subscriptions/subscriptions.module.ts b/src/app/shared/subscriptions/subscriptions.module.ts index 9b72493f67..122bf5ca8d 100644 --- a/src/app/shared/subscriptions/subscriptions.module.ts +++ b/src/app/shared/subscriptions/subscriptions.module.ts @@ -2,16 +2,16 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ReactiveFormsModule } from '@angular/forms'; -import { SubscriptionViewComponent } from './components/subscription-view/subscription-view.component'; -import { SubscriptionModalComponent } from './components/subscription-modal/subscription-modal.component'; -import { SubscriptionEditModalComponent } from './components/subscription-edit-modal/subscription-edit-modal.component'; +import { SubscriptionViewComponent } from './subscription-view/subscription-view.component'; +import { SubscriptionModalComponent } from './subscription-modal/subscription-modal.component'; import { TranslateModule } from '@ngx-translate/core'; import { RouterModule } from '@angular/router'; +import { SharedModule } from '../shared.module'; +import { NgbModalModule } from '@ng-bootstrap/ng-bootstrap'; const COMPONENTS = [ SubscriptionViewComponent, - SubscriptionModalComponent, - SubscriptionEditModalComponent, + SubscriptionModalComponent ]; @NgModule({ @@ -20,12 +20,15 @@ const COMPONENTS = [ ], imports: [ CommonModule, + NgbModalModule, ReactiveFormsModule, TranslateModule, - RouterModule + RouterModule, + SharedModule ], exports: [ ...COMPONENTS ] }) -export class SubscriptionsModule { } +export class SubscriptionsModule { +} diff --git a/src/app/shared/testing/subscriptions-data.mock.ts b/src/app/shared/testing/subscriptions-data.mock.ts index 971f69786e..191fc46252 100644 --- a/src/app/shared/testing/subscriptions-data.mock.ts +++ b/src/app/shared/testing/subscriptions-data.mock.ts @@ -4435,7 +4435,7 @@ export const findByEPersonAndDsoResEmpty = { 'page': [] }; -export const subscription = { +export const subscriptionMock = { 'id': 21, 'type': 'subscription', 'subscriptionParameterList': [ @@ -4450,18 +4450,41 @@ export const subscription = { 'value': 'M' } ], - 'subscriptionType': 'content', + 'subscriptionType': 'test1', '_links': { 'dSpaceObject': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21/dSpaceObject' + 'href': 'https://dspace/server/api/core/subscriptions/21/dSpaceObject' }, 'ePerson': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21/ePerson' + 'href': 'https://dspace/server/api/core/subscriptions/21/ePerson' }, 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21' + 'href': 'https://dspace/server/api/core/subscriptions/21' } } }; +export const subscriptionMock2 = { + 'id': 21, + 'type': 'subscription', + 'subscriptionParameterList': [ + { + 'id': 77, + 'name': 'frequency', + 'value': 'D' + }, + ], + 'subscriptionType': 'test2', + '_links': { + 'dSpaceObject': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21/ePerson' + }, + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21' + } + } +}; diff --git a/src/app/subscriptions-page/subscriptions-page.component.html b/src/app/subscriptions-page/subscriptions-page.component.html index 9d1e6d504e..56e4980ca7 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.html +++ b/src/app/subscriptions-page/subscriptions-page.component.html @@ -4,13 +4,12 @@

{{'subscriptions.title' | translate}}

- + @@ -25,16 +24,20 @@ - +
-
+ {{ 'subscriptions.table.empty.message' | translate }} -
+
diff --git a/src/app/subscriptions-page/subscriptions-page.component.ts b/src/app/subscriptions-page/subscriptions-page.component.ts index 2951cf037c..a99daa9d68 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.ts +++ b/src/app/subscriptions-page/subscriptions-page.component.ts @@ -1,6 +1,8 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { BehaviorSubject, combineLatestWith, Observable, of, shareReplay, Subscription as rxSubscription } from 'rxjs'; -import { combineLatest, map, switchMap, take, tap } from 'rxjs/operators'; +import { Component, OnInit } from '@angular/core'; + +import { BehaviorSubject, combineLatestWith, Observable, shareReplay } from 'rxjs'; +import { map, switchMap, take, tap } from 'rxjs/operators'; + import { Subscription } from '../shared/subscriptions/models/subscription.model'; import { buildPaginatedList, PaginatedList } from '../core/data/paginated-list.model'; import { SubscriptionService } from '../shared/subscriptions/subscription.service'; @@ -16,7 +18,7 @@ import { getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; templateUrl: './subscriptions-page.component.html', styleUrls: ['./subscriptions-page.component.scss'] }) -export class SubscriptionsPageComponent implements OnInit, OnDestroy { +export class SubscriptionsPageComponent implements OnInit { /** * The subscriptions to show on this page, as an Observable list. @@ -33,11 +35,6 @@ export class SubscriptionsPageComponent implements OnInit, OnDestroy { currentPage: 1 }); - /** - * Subscription to be unsubscribed - */ - sub: rxSubscription; - /** * A boolean representing if is loading */ @@ -69,7 +66,11 @@ export class SubscriptionsPageComponent implements OnInit, OnDestroy { this.ePersonId = ePersonId; }),*/ ); - const currentPagination$ = this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( + this.retrieveSubscriptions(); + } + + private retrieveSubscriptions() { + this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( tap(console.log), combineLatestWith(this.ePersonId$), tap(() => {this.loading$.next(true);}), @@ -77,10 +78,12 @@ export class SubscriptionsPageComponent implements OnInit, OnDestroy { currentPage: currentPagination.currentPage, elementsPerPage: currentPagination.pageSize })), + getFirstSucceededRemoteDataPayload(), tap((x) => console.log('find', x)), // getFirstSucceededRemoteDataPayload(), ).subscribe({ next: (res: any) => { + console.log('next',res); this.subscriptions$.next(res); this.loading$.next(false); }, @@ -89,41 +92,11 @@ export class SubscriptionsPageComponent implements OnInit, OnDestroy { } }); } - /** * When an action is made and the information is changed refresh the information */ refresh(): void { - /*this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( - take(1), - switchMap((findListOptions) => { - this.loading$.next(true); - return this.subscriptionService.findByEPerson(this.ePersonId,{ - currentPage: findListOptions.currentPage, - elementsPerPage: findListOptions.pageSize - }); - } - ) - ).subscribe({ - next: (res: any) => { - this.subscriptions$.next(res); - this.loading$.next(false); - }, - error: () => { - this.loading$.next(false); - } - });*/ - } - - /** - * Unsubscribe from pagination subscription - */ - ngOnDestroy(): void { - this.sub.unsubscribe(); - } - - obs(v) { - return of(v); + this.retrieveSubscriptions(); } } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 2af33eb11d..ea0c87b858 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1450,6 +1450,13 @@ "confirmation-modal.delete-profile.confirm": "Delete", + "confirmation-modal.delete-subscription.header": "Delete Subscription", + + "confirmation-modal.delete-subscription.info": "Are you sure you want to delete subscription for \"{{ dsoName }}\"", + + "confirmation-modal.delete-subscription.cancel": "Cancel", + + "confirmation-modal.delete-subscription.confirm": "Delete", "error.bitstream": "Error fetching bitstream", @@ -4512,6 +4519,20 @@ "subscriptions.frequency.W": "Weekly", + + + "subscriptions.modal.create.success": "Subscribed to {{ type }} successfully.", + + "subscriptions.modal.delete.success": "Subscription deleted successfully", + + "subscriptions.modal.update.success": "Subscription to {{ type }} updated successfully", + + "subscriptions.modal.create.error": "An error occurs during the subscription creation", + + "subscriptions.modal.delete.error": "An error occurs during the subscription delete", + + "subscriptions.modal.update.error": "An error occurs during the subscription update", + "subscriptions.table.dso": "Subject", "subscriptions.table.subscription_type": "Subscription Type", From 88e191e961a9143e5e7ca3c435a062820342e48a Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 28 Dec 2022 11:28:18 +0100 Subject: [PATCH 06/29] [CST-7757] Fix lint errors --- ...page-subscription-button.component.spec.ts | 4 ++-- .../subscription-modal.component.spec.ts | 10 ++++----- .../subscription-modal.component.ts | 22 +++++++++---------- .../subscription-view.component.ts | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.spec.ts b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.spec.ts index 7dcdf84235..726854778d 100644 --- a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.spec.ts +++ b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.spec.ts @@ -61,7 +61,7 @@ describe('DsoPageSubscriptionButtonComponent', () => { describe('when is authorized', () => { beforeEach(() => { - authorizationService.isAuthorized.and.returnValue(observableOf(true)) + authorizationService.isAuthorized.and.returnValue(observableOf(true)); fixture.detectChanges(); }); @@ -72,7 +72,7 @@ describe('DsoPageSubscriptionButtonComponent', () => { describe('when is not authorized', () => { beforeEach(() => { - authorizationService.isAuthorized.and.returnValue(observableOf(false)) + authorizationService.isAuthorized.and.returnValue(observableOf(false)); fixture.detectChanges(); }); diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.spec.ts b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.spec.ts index c693f1e168..05fa1c4a77 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.spec.ts +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.spec.ts @@ -106,7 +106,7 @@ describe('SubscriptionModalComponent', () => { fixture = TestBed.createComponent(SubscriptionModalComponent); component = fixture.componentInstance; component.dso = mockItem; - (component as any).subscriptionDefaultTypes = ['test1', 'test2'] + (component as any).subscriptionDefaultTypes = ['test1', 'test2']; de = fixture.debugElement; }); @@ -128,7 +128,7 @@ describe('SubscriptionModalComponent', () => { (component as any).frequencyDefaultValues.forEach((frequency) => { expect(component.subscriptionForm.get('test1').get('frequencies').get(frequency)).toBeTruthy(); expect(component.subscriptionForm.get('test2').get('frequencies').get(frequency)).toBeTruthy(); - }) + }); }); }); @@ -151,7 +151,7 @@ describe('SubscriptionModalComponent', () => { expect(component.subscriptionForm.get('test1').get('frequencies').get(frequency)).toBeTruthy(); expect(component.subscriptionForm.get('test2').get('frequencies').get(frequency)).toBeTruthy(); - }) + }); expect(component.subscriptionForm.get('test1').get('frequencies').get('D').value).toBeTrue(); expect(component.subscriptionForm.get('test1').get('frequencies').get('M').value).toBeTrue(); expect(component.subscriptionForm.get('test1').get('frequencies').get('W').value).toBeFalse(); @@ -169,7 +169,7 @@ describe('SubscriptionModalComponent', () => { component = fixture.componentInstance; component.dso = mockItem; component.subscription = subscriptionMock as any; - (component as any).subscriptionDefaultTypes = ['test1', 'test2'] + (component as any).subscriptionDefaultTypes = ['test1', 'test2']; de = fixture.debugElement; fixture.detectChanges(); }); @@ -184,7 +184,7 @@ describe('SubscriptionModalComponent', () => { expect(component.subscriptionForm.get('test1')).toBeTruthy(); (component as any).frequencyDefaultValues.forEach((frequency) => { expect(component.subscriptionForm.get('test1').get('frequencies').get(frequency)).toBeTruthy(); - }) + }); expect(component.subscriptionForm.get('test1').get('frequencies').get('D').value).toBeTrue(); expect(component.subscriptionForm.get('test1').get('frequencies').get('M').value).toBeTrue(); expect(component.subscriptionForm.get('test1').get('frequencies').get('W').value).toBeFalse(); diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts index 7fcba2d16b..6f1d1fd158 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts @@ -104,13 +104,13 @@ export class SubscriptionModalComponent implements OnInit { private initFormByAllSubscriptions(): void { this.subscriptionForm = new FormGroup({}); for (let t of this.subscriptionDefaultTypes) { - const formGroup = new FormGroup({}) + const formGroup = new FormGroup({}); formGroup.addControl('subscriptionId', this.formBuilder.control('')); formGroup.addControl('frequencies', this.formBuilder.group({})); for (let f of this.frequencyDefaultValues) { - (formGroup.controls['frequencies'] as FormGroup).addControl(f, this.formBuilder.control(false)); + (formGroup.controls.frequencies as FormGroup).addControl(f, this.formBuilder.control(false)); } - this.subscriptionForm.addControl(t, formGroup) + this.subscriptionForm.addControl(t, formGroup); } this.initFormDataBySubscriptions(); @@ -120,13 +120,13 @@ export class SubscriptionModalComponent implements OnInit { * If the subscription is passed start the form with the information of subscription */ initFormByGivenSubscription(): void { - const formGroup = new FormGroup({}) + const formGroup = new FormGroup({}); formGroup.addControl('subscriptionId', this.formBuilder.control(this.subscription.id)); formGroup.addControl('frequencies', this.formBuilder.group({})); (formGroup.get('frequencies') as FormGroup).addValidators(Validators.required); for (let f of this.frequencyDefaultValues) { const value = findIndex(this.subscription.subscriptionParameterList, ['value', f]) !== -1; - (formGroup.controls['frequencies'] as FormGroup).addControl(f, this.formBuilder.control(value)); + (formGroup.controls.frequencies as FormGroup).addControl(f, this.formBuilder.control(value)); } this.subscriptionForm = this.formBuilder.group({ @@ -149,9 +149,9 @@ export class SubscriptionModalComponent implements OnInit { const type = subscription.subscriptionType; const subscriptionGroup: FormGroup = this.subscriptionForm.get(type) as FormGroup; if (isNotEmpty(subscriptionGroup)) { - subscriptionGroup.controls['subscriptionId'].setValue(subscription.id); + subscriptionGroup.controls.subscriptionId.setValue(subscription.id); for (let parameter of subscription.subscriptionParameterList.filter((p) => p.name === 'frequency')) { - (subscriptionGroup.controls['frequencies'] as FormGroup).controls[parameter.value]?.setValue(true); + (subscriptionGroup.controls.frequencies as FormGroup).controls[parameter.value]?.setValue(true); } } } @@ -177,9 +177,9 @@ export class SubscriptionModalComponent implements OnInit { const subscriptionGroup: FormGroup = this.subscriptionForm.controls[subscriptionType] as FormGroup; if (subscriptionGroup.touched && subscriptionGroup.dirty) { const body = this.createBody( - subscriptionGroup.controls['subscriptionId'].value, + subscriptionGroup.controls.subscriptionId.value, subscriptionType, - subscriptionGroup.controls['frequencies'] as FormGroup + subscriptionGroup.controls.frequencies as FormGroup ); if (isNotEmpty(body.id)) { @@ -197,7 +197,7 @@ export class SubscriptionModalComponent implements OnInit { mergeMap((subscriptionBody) => { return this.subscriptionService.createSubscription(subscriptionBody, this.ePersonId, this.dso.uuid).pipe( getFirstCompletedRemoteData() - ) + ); }), tap((res: RemoteData) => { if (res.hasSucceeded) { @@ -215,7 +215,7 @@ export class SubscriptionModalComponent implements OnInit { mergeMap((subscriptionBody) => { return this.subscriptionService.updateSubscription(subscriptionBody, this.ePersonId, this.dso.uuid).pipe( getFirstCompletedRemoteData() - ) + ); }), tap((res: RemoteData) => { if (res.hasSucceeded) { diff --git a/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts b/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts index 4844be5b79..b8aa4d2375 100644 --- a/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts +++ b/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts @@ -100,7 +100,7 @@ export class SubscriptionViewComponent { this.modalRef.componentInstance.subscription = this.subscription; this.modalRef.componentInstance.updateSubscription.pipe(take(1)).subscribe((subscription: Subscription) => { this.subscription = subscription; - }) + }); } } From 88eefc6b79d40f5050c3c8bf5a085aebea37101f Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 28 Dec 2022 15:29:13 +0100 Subject: [PATCH 07/29] [CST-7757] fix tests --- .../subscription-modal.component.ts | 3 +- .../testing/notifications-service.stub.ts | 3 - .../shared/testing/subscriptions-data.mock.ts | 4578 +---------------- .../subscriptions-page.component.html | 15 +- .../subscriptions-page.component.spec.ts | 118 +- .../subscriptions-page.component.ts | 30 +- 6 files changed, 202 insertions(+), 4545 deletions(-) diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts index 6f1d1fd158..23faf33c54 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts @@ -2,11 +2,11 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; - import { BehaviorSubject, combineLatest, from, shareReplay } from 'rxjs'; import { map, mergeMap, take, tap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import findIndex from 'lodash/findIndex'; import { Subscription } from '../models/subscription.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; @@ -17,7 +17,6 @@ import { RemoteData } from '../../../core/data/remote-data'; import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators'; import { AuthService } from '../../../core/auth/auth.service'; import { isNotEmpty } from '../../empty.util'; -import { findIndex } from 'lodash'; @Component({ selector: 'ds-subscription-modal', diff --git a/src/app/shared/testing/notifications-service.stub.ts b/src/app/shared/testing/notifications-service.stub.ts index 829b65fc1d..154c5b4351 100644 --- a/src/app/shared/testing/notifications-service.stub.ts +++ b/src/app/shared/testing/notifications-service.stub.ts @@ -9,9 +9,6 @@ export class NotificationsServiceStub { remove = jasmine.createSpy('remove'); removeAll = jasmine.createSpy('removeAll'); - notificationWithAnchor() { - } - private getDefaultOptions(): NotificationOptions { return new NotificationOptions(); } diff --git a/src/app/shared/testing/subscriptions-data.mock.ts b/src/app/shared/testing/subscriptions-data.mock.ts index 191fc46252..8a4afc6d52 100644 --- a/src/app/shared/testing/subscriptions-data.mock.ts +++ b/src/app/shared/testing/subscriptions-data.mock.ts @@ -1,4467 +1,135 @@ -export const findAllSubscriptionRes = { - 'type': { - 'value': 'paginated-list' - }, - 'pageInfo': { - 'elementsPerPage': 10, - 'totalElements': 10, - 'totalPages': 1, - 'currentPage': 1 - }, - '_links': { - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions?page=0&size=10' - }, - 'search': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/search' - }, - 'page': [ - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21' - }, - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22' - }, - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/23' - }, - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/24' - }, - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/25' - }, - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/26' - }, - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/27' - }, - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/30' - }, - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/31' - }, - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/32' - } - ] - }, - 'page': [ - { - 'id': 21, - 'subscriptionType': 'content', - 'subscriptionParameterList': [ - { - 'id': 77, - 'name': 'frequency', - 'value': 'D' - }, - { - 'id': 78, - 'name': 'frequency', - 'value': 'M' - } - ], - '_links': { - 'dSpaceObject': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21/dSpaceObject' - }, - 'ePerson': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21/ePerson' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21' - } - }, - '_embedded': { - 'ePerson': { - 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'name': 'dspacedemo+admin@gmail.com', - 'handle': null, - 'metadata': { - 'dspace.agreements.cookies': [ - { - 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.agreements.end-user': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.firstname': [ - { - 'value': 'Demo', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.lastname': [ - { - 'value': 'Site Administrator', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'netid': null, - 'lastActive': '2021-09-01T12:06:19.000+00:00', - 'canLogIn': true, - 'email': 'dspacedemo+admin@gmail.com', - 'requireCertificate': false, - 'selfRegistered': false, - 'type': 'eperson', - '_links': { - 'groups': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' - } - } - }, - 'dSpaceObject': { - 'id': '092b59e8-8159-4e70-98b5-93ec60bd3431', - 'uuid': '092b59e8-8159-4e70-98b5-93ec60bd3431', - 'name': 'Bollini, Andrea', - 'handle': '123456789/43', - 'metadata': { - 'creativework.datePublished': [ - { - 'value': null, - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.orcid.access-token': [ - { - 'value': 'a456a1c3-1c9e-45f8-9e11-f273fa58de2e', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.authenticated': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.orcid.refresh-token': [ - { - 'value': '13a63f03-7c49-4dad-b39b-070f73cc7ac1', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.scope': [ - { - 'value': '/authenticate', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '/read-limited', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': '/activities/update', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - }, - { - 'value': '/person/update', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 3, - 'securityLevel': 0 - } - ], - 'dspace.orcid.sync-fundings': [ - { - 'value': 'ALL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.sync-mode': [ - { - 'value': 'MANUAL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.sync-profile': [ - { - 'value': 'AFFILIATION', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'EDUCATION', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': 'BIOGRAPHICAL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - }, - { - 'value': 'IDENTIFIERS', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 3, - 'securityLevel': 0 - } - ], - 'cris.orcid.sync-projects': [ - { - 'value': 'ALL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.sync-publications': [ - { - 'value': 'ALL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.webhook': [ - { - 'value': '2021-05-26T12:47:27.971367', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.object.owner': [ - { - 'value': 'Demo Site Administrator', - 'language': null, - 'authority': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'confidence': 600, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.policy.group': [ - { - 'value': 'Administrator', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.sourceId': [ - { - 'value': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.workspace.shared': [ - { - 'value': 'fg', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'crisrp.country': [ - { - 'value': 'IT', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'crisrp.education': [ - { - 'value': 'Università degli Studi di Milano Bicocca', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'Sapienza Università di Roma', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'crisrp.education.end': [ - { - 'value': '2008', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '2003', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'crisrp.education.role': [ - { - 'value': 'Master post experience 2nd level', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'Graduate Studies - Mathematics, Physics', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'crisrp.education.start': [ - { - 'value': '2007', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '1998', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'crisrp.name.translated': [ - { - 'value': null, - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0, - 'securityLevel': 0 - } - ], - 'crisrp.site.title': [ - { - 'value': 'LinkedIn', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'GitHub', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'dc.date.accessioned': [ - { - 'value': '2020-09-14T09:36:02Z', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.date.available': [ - { - 'value': '2020-09-14T09:36:02Z', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.description.abstract': [ - { - 'value': 'I’m responsible for all the technological aspects of the company proposal, from the final solutions to tools, methodologies and technologies adopted for the production and support activities. Among my responsibilities, I define the infrastructure that best fits the project requirements. We provide support on premis on the customer data center and worldwide cloud providers. Our hosted solutions are powered by Amazon Web Services, our experience in their services allow us to offer best in class solutions, scalable and cost-optimized.\n\nI’m in charge of the planning, estimation and execution of the projects from the technical perspective, and of the preparation of technical annexes for national and international tenders.\n\nI lead the teams of software architects and developers, assuring the adoption of best practices and up-to-date technologies. I’m in charge of the scouting and integration of new technologies and products within our solutions with a particular focus on Open Source and Open Standards. I’m directly involved with open-source and domain communities to assure that our solutions remain aligned with the international trends both from the technical perspective and for the functional requirements.', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.description.provenance': [ - { - 'value': 'Made available in DSpace on 2020-09-14T09:36:02Z (GMT). No. of bitstreams: 0', - 'language': 'en', - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.identifier.uri': [ - { - 'value': 'https://dspacecris7.4science.cloud/handle/123456789/43', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.title': [ - { - 'value': 'Bollini, Andrea', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.entity.type': [ - { - 'value': 'Person', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'oairecerif.affiliation.endDate': [ - { - 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '2016', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': '2016', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - } - ], - 'oairecerif.affiliation.role': [ - { - 'value': 'CTO', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'Head of Open Source & Open Standards Strategy', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': 'Head of Open Source & Open Standards Strategy', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - } - ], - 'oairecerif.affiliation.startDate': [ - { - 'value': '2016', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '2012', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': '2012', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - } - ], - 'oairecerif.identifier.url': [ - { - 'value': 'https://www.linkedin.com/in/andreabollini/', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'https://github.com/abollini', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'oairecerif.person.affiliation': [ - { - 'value': '4Science', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'CINECA', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': 'CINECA', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - } - ], - 'oairecerif.person.gender': [ - { - 'value': 'm', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.affiliation.name': [ - { - 'value': '4Science', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.email': [ - { - 'value': 'andrea.bollini@4science.it', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.familyName': [ - { - 'value': 'Bollini', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.givenName': [ - { - 'value': 'Andrea', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.identifier.orcid': [ - { - 'value': '0000-0003-0864-8867', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.identifier.scopus-author-id': [ - { - 'value': '55484808800', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.jobTitle': [ - { - 'value': 'CTO', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.knowsLanguage': [ - { - 'value': 'en', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'en', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ] - }, - 'inArchive': true, - 'discoverable': true, - 'withdrawn': false, - 'lastModified': '2021-08-23T10:44:57.768+00:00', - 'entityType': 'Person', - 'type': 'item', - '_links': { - 'bundles': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/bundles' - }, - 'mappedCollections': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/mappedCollections' - }, - 'owningCollection': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/owningCollection' - }, - 'relationships': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/relationships' - }, - 'version': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/version' - }, - 'templateItemOf': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/templateItemOf' - }, - 'metrics': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/metrics' - }, - 'thumbnail': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/thumbnail' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431' - } - } - } - }, - 'type': 'subscription' - }, - { - 'id': 22, - 'subscriptionType': 'content', - 'subscriptionParameterList': [ - { - 'id': 138, - 'name': 'frequency', - 'value': 'D' - } - ], - '_links': { - 'dSpaceObject': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22/dSpaceObject' - }, - 'ePerson': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22/ePerson' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22' - } - }, - '_embedded': { - 'ePerson': { - 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'name': 'dspacedemo+admin@gmail.com', - 'handle': null, - 'metadata': { - 'dspace.agreements.cookies': [ - { - 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.agreements.end-user': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.firstname': [ - { - 'value': 'Demo', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.lastname': [ - { - 'value': 'Site Administrator', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'netid': null, - 'lastActive': '2021-09-01T12:06:19.000+00:00', - 'canLogIn': true, - 'email': 'dspacedemo+admin@gmail.com', - 'requireCertificate': false, - 'selfRegistered': false, - 'type': 'eperson', - '_links': { - 'groups': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' - } - } - }, - 'dSpaceObject': { - 'id': '092b59e8-8159-4e70-98b5-93ec60bd3431', - 'uuid': '092b59e8-8159-4e70-98b5-93ec60bd3431', - 'name': 'Bollini, Andrea', - 'handle': '123456789/43', - 'metadata': { - 'creativework.datePublished': [ - { - 'value': null, - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.orcid.access-token': [ - { - 'value': 'a456a1c3-1c9e-45f8-9e11-f273fa58de2e', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.authenticated': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.orcid.refresh-token': [ - { - 'value': '13a63f03-7c49-4dad-b39b-070f73cc7ac1', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.scope': [ - { - 'value': '/authenticate', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '/read-limited', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': '/activities/update', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - }, - { - 'value': '/person/update', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 3, - 'securityLevel': 0 - } - ], - 'dspace.orcid.sync-fundings': [ - { - 'value': 'ALL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.sync-mode': [ - { - 'value': 'MANUAL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.sync-profile': [ - { - 'value': 'AFFILIATION', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'EDUCATION', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': 'BIOGRAPHICAL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - }, - { - 'value': 'IDENTIFIERS', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 3, - 'securityLevel': 0 - } - ], - 'cris.orcid.sync-projects': [ - { - 'value': 'ALL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.sync-publications': [ - { - 'value': 'ALL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.webhook': [ - { - 'value': '2021-05-26T12:47:27.971367', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.object.owner': [ - { - 'value': 'Demo Site Administrator', - 'language': null, - 'authority': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'confidence': 600, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.policy.group': [ - { - 'value': 'Administrator', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.sourceId': [ - { - 'value': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.workspace.shared': [ - { - 'value': 'fg', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'crisrp.country': [ - { - 'value': 'IT', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'crisrp.education': [ - { - 'value': 'Università degli Studi di Milano Bicocca', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'Sapienza Università di Roma', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'crisrp.education.end': [ - { - 'value': '2008', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '2003', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'crisrp.education.role': [ - { - 'value': 'Master post experience 2nd level', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'Graduate Studies - Mathematics, Physics', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'crisrp.education.start': [ - { - 'value': '2007', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '1998', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'crisrp.name.translated': [ - { - 'value': null, - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0, - 'securityLevel': 0 - } - ], - 'crisrp.site.title': [ - { - 'value': 'LinkedIn', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'GitHub', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'dc.date.accessioned': [ - { - 'value': '2020-09-14T09:36:02Z', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.date.available': [ - { - 'value': '2020-09-14T09:36:02Z', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.description.abstract': [ - { - 'value': 'I’m responsible for all the technological aspects of the company proposal, from the final solutions to tools, methodologies and technologies adopted for the production and support activities. Among my responsibilities, I define the infrastructure that best fits the project requirements. We provide support on premis on the customer data center and worldwide cloud providers. Our hosted solutions are powered by Amazon Web Services, our experience in their services allow us to offer best in class solutions, scalable and cost-optimized.\n\nI’m in charge of the planning, estimation and execution of the projects from the technical perspective, and of the preparation of technical annexes for national and international tenders.\n\nI lead the teams of software architects and developers, assuring the adoption of best practices and up-to-date technologies. I’m in charge of the scouting and integration of new technologies and products within our solutions with a particular focus on Open Source and Open Standards. I’m directly involved with open-source and domain communities to assure that our solutions remain aligned with the international trends both from the technical perspective and for the functional requirements.', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.description.provenance': [ - { - 'value': 'Made available in DSpace on 2020-09-14T09:36:02Z (GMT). No. of bitstreams: 0', - 'language': 'en', - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.identifier.uri': [ - { - 'value': 'https://dspacecris7.4science.cloud/handle/123456789/43', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.title': [ - { - 'value': 'Bollini, Andrea', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.entity.type': [ - { - 'value': 'Person', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'oairecerif.affiliation.endDate': [ - { - 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '2016', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': '2016', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - } - ], - 'oairecerif.affiliation.role': [ - { - 'value': 'CTO', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'Head of Open Source & Open Standards Strategy', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': 'Head of Open Source & Open Standards Strategy', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - } - ], - 'oairecerif.affiliation.startDate': [ - { - 'value': '2016', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '2012', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': '2012', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - } - ], - 'oairecerif.identifier.url': [ - { - 'value': 'https://www.linkedin.com/in/andreabollini/', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'https://github.com/abollini', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'oairecerif.person.affiliation': [ - { - 'value': '4Science', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'CINECA', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': 'CINECA', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - } - ], - 'oairecerif.person.gender': [ - { - 'value': 'm', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.affiliation.name': [ - { - 'value': '4Science', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.email': [ - { - 'value': 'andrea.bollini@4science.it', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.familyName': [ - { - 'value': 'Bollini', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.givenName': [ - { - 'value': 'Andrea', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.identifier.orcid': [ - { - 'value': '0000-0003-0864-8867', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.identifier.scopus-author-id': [ - { - 'value': '55484808800', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.jobTitle': [ - { - 'value': 'CTO', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.knowsLanguage': [ - { - 'value': 'en', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'en', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ] - }, - 'inArchive': true, - 'discoverable': true, - 'withdrawn': false, - 'lastModified': '2021-08-23T10:44:57.768+00:00', - 'entityType': 'Person', - 'type': 'item', - '_links': { - 'bundles': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/bundles' - }, - 'mappedCollections': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/mappedCollections' - }, - 'owningCollection': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/owningCollection' - }, - 'relationships': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/relationships' - }, - 'version': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/version' - }, - 'templateItemOf': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/templateItemOf' - }, - 'metrics': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/metrics' - }, - 'thumbnail': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/thumbnail' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431' - } - } - } - }, - 'type': 'subscription' - }, - { - 'id': 23, - 'subscriptionType': 'content', - 'subscriptionParameterList': [ - { - 'id': 80, - 'name': 'frequency', - 'value': 'D' - }, - { - 'id': 81, - 'name': 'frequency', - 'value': 'M' - } - ], - '_links': { - 'dSpaceObject': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/23/dSpaceObject' - }, - 'ePerson': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/23/ePerson' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/23' - } - }, - '_embedded': { - 'ePerson': { - 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'name': 'dspacedemo+admin@gmail.com', - 'handle': null, - 'metadata': { - 'dspace.agreements.cookies': [ - { - 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.agreements.end-user': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.firstname': [ - { - 'value': 'Demo', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.lastname': [ - { - 'value': 'Site Administrator', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'netid': null, - 'lastActive': '2021-09-01T12:06:19.000+00:00', - 'canLogIn': true, - 'email': 'dspacedemo+admin@gmail.com', - 'requireCertificate': false, - 'selfRegistered': false, - 'type': 'eperson', - '_links': { - 'groups': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' - } - } - }, - 'dSpaceObject': { - 'id': '092b59e8-8159-4e70-98b5-93ec60bd3431', - 'uuid': '092b59e8-8159-4e70-98b5-93ec60bd3431', - 'name': 'Bollini, Andrea', - 'handle': '123456789/43', - 'metadata': { - 'creativework.datePublished': [ - { - 'value': null, - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.orcid.access-token': [ - { - 'value': 'a456a1c3-1c9e-45f8-9e11-f273fa58de2e', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.authenticated': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.orcid.refresh-token': [ - { - 'value': '13a63f03-7c49-4dad-b39b-070f73cc7ac1', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.scope': [ - { - 'value': '/authenticate', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '/read-limited', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': '/activities/update', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - }, - { - 'value': '/person/update', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 3, - 'securityLevel': 0 - } - ], - 'dspace.orcid.sync-fundings': [ - { - 'value': 'ALL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.sync-mode': [ - { - 'value': 'MANUAL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.sync-profile': [ - { - 'value': 'AFFILIATION', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'EDUCATION', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': 'BIOGRAPHICAL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - }, - { - 'value': 'IDENTIFIERS', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 3, - 'securityLevel': 0 - } - ], - 'cris.orcid.sync-projects': [ - { - 'value': 'ALL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.sync-publications': [ - { - 'value': 'ALL', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.orcid.webhook': [ - { - 'value': '2021-05-26T12:47:27.971367', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.object.owner': [ - { - 'value': 'Demo Site Administrator', - 'language': null, - 'authority': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'confidence': 600, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.policy.group': [ - { - 'value': 'Administrator', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.sourceId': [ - { - 'value': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'cris.workspace.shared': [ - { - 'value': 'fg', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'crisrp.country': [ - { - 'value': 'IT', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'crisrp.education': [ - { - 'value': 'Università degli Studi di Milano Bicocca', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'Sapienza Università di Roma', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'crisrp.education.end': [ - { - 'value': '2008', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '2003', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'crisrp.education.role': [ - { - 'value': 'Master post experience 2nd level', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'Graduate Studies - Mathematics, Physics', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'crisrp.education.start': [ - { - 'value': '2007', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '1998', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'crisrp.name.translated': [ - { - 'value': null, - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0, - 'securityLevel': 0 - } - ], - 'crisrp.site.title': [ - { - 'value': 'LinkedIn', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'GitHub', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'dc.date.accessioned': [ - { - 'value': '2020-09-14T09:36:02Z', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.date.available': [ - { - 'value': '2020-09-14T09:36:02Z', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.description.abstract': [ - { - 'value': 'I’m responsible for all the technological aspects of the company proposal, from the final solutions to tools, methodologies and technologies adopted for the production and support activities. Among my responsibilities, I define the infrastructure that best fits the project requirements. We provide support on premis on the customer data center and worldwide cloud providers. Our hosted solutions are powered by Amazon Web Services, our experience in their services allow us to offer best in class solutions, scalable and cost-optimized.\n\nI’m in charge of the planning, estimation and execution of the projects from the technical perspective, and of the preparation of technical annexes for national and international tenders.\n\nI lead the teams of software architects and developers, assuring the adoption of best practices and up-to-date technologies. I’m in charge of the scouting and integration of new technologies and products within our solutions with a particular focus on Open Source and Open Standards. I’m directly involved with open-source and domain communities to assure that our solutions remain aligned with the international trends both from the technical perspective and for the functional requirements.', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.description.provenance': [ - { - 'value': 'Made available in DSpace on 2020-09-14T09:36:02Z (GMT). No. of bitstreams: 0', - 'language': 'en', - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.identifier.uri': [ - { - 'value': 'https://dspacecris7.4science.cloud/handle/123456789/43', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dc.title': [ - { - 'value': 'Bollini, Andrea', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'dspace.entity.type': [ - { - 'value': 'Person', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'oairecerif.affiliation.endDate': [ - { - 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '2016', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': '2016', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - } - ], - 'oairecerif.affiliation.role': [ - { - 'value': 'CTO', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'Head of Open Source & Open Standards Strategy', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': 'Head of Open Source & Open Standards Strategy', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - } - ], - 'oairecerif.affiliation.startDate': [ - { - 'value': '2016', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': '2012', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': '2012', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - } - ], - 'oairecerif.identifier.url': [ - { - 'value': 'https://www.linkedin.com/in/andreabollini/', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'https://github.com/abollini', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ], - 'oairecerif.person.affiliation': [ - { - 'value': '4Science', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'CINECA', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - }, - { - 'value': 'CINECA', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 2, - 'securityLevel': 0 - } - ], - 'oairecerif.person.gender': [ - { - 'value': 'm', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.affiliation.name': [ - { - 'value': '4Science', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.email': [ - { - 'value': 'andrea.bollini@4science.it', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.familyName': [ - { - 'value': 'Bollini', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.givenName': [ - { - 'value': 'Andrea', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.identifier.orcid': [ - { - 'value': '0000-0003-0864-8867', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.identifier.scopus-author-id': [ - { - 'value': '55484808800', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.jobTitle': [ - { - 'value': 'CTO', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - } - ], - 'person.knowsLanguage': [ - { - 'value': 'en', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 0, - 'securityLevel': 0 - }, - { - 'value': 'en', - 'language': null, - 'authority': null, - 'confidence': 0, - 'place': 1, - 'securityLevel': 0 - } - ] - }, - 'inArchive': true, - 'discoverable': true, - 'withdrawn': false, - 'lastModified': '2021-08-23T10:44:57.768+00:00', - 'entityType': 'Person', - 'type': 'item', - '_links': { - 'bundles': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/bundles' - }, - 'mappedCollections': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/mappedCollections' - }, - 'owningCollection': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/owningCollection' - }, - 'relationships': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/relationships' - }, - 'version': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/version' - }, - 'templateItemOf': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/templateItemOf' - }, - 'metrics': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/metrics' - }, - 'thumbnail': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431/thumbnail' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/092b59e8-8159-4e70-98b5-93ec60bd3431' - } - } - } - }, - 'type': 'subscription' - }, - { - 'id': 24, - 'subscriptionType': 'content', - 'subscriptionParameterList': [ - { - 'id': 82, - 'name': 'frequency', - 'value': 'D' - }, - { - 'id': 83, - 'name': 'frequency', - 'value': 'M' - } - ], - '_links': { - 'dSpaceObject': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/24/dSpaceObject' - }, - 'ePerson': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/24/ePerson' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/24' - } - }, - '_embedded': { - 'ePerson': { - 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'name': 'dspacedemo+admin@gmail.com', - 'handle': null, - 'metadata': { - 'dspace.agreements.cookies': [ - { - 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.agreements.end-user': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.firstname': [ - { - 'value': 'Demo', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.lastname': [ - { - 'value': 'Site Administrator', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'netid': null, - 'lastActive': '2021-09-01T12:06:19.000+00:00', - 'canLogIn': true, - 'email': 'dspacedemo+admin@gmail.com', - 'requireCertificate': false, - 'selfRegistered': false, - 'type': 'eperson', - '_links': { - 'groups': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' - } - } - }, - 'dSpaceObject': { - 'id': '90f3b9ce-db65-479c-90d7-8c794abf942c', - 'uuid': '90f3b9ce-db65-479c-90d7-8c794abf942c', - 'name': 'DSpace-CRIS', - 'handle': '123456789/34', - 'metadata': { - 'crispj.coinvestigators': [ - { - 'value': 'Mornati, Susanna', - 'language': null, - 'authority': '1325093a-1ef4-4d87-920d-02ce544fea00', - 'confidence': 600, - 'place': 0 - }, - { - 'value': 'Lombardi, Corrado', - 'language': null, - 'authority': 'b5ad6864-012d-4989-8e0d-4acfa1156fd9', - 'confidence': 600, - 'place': 1 - } - ], - 'crispj.coordinator': [ - { - 'value': '4Science', - 'language': null, - 'authority': 'a14ba215-c0f0-4b74-b21a-06359bfabd45', - 'confidence': 600, - 'place': 0 - } - ], - 'crispj.investigator': [ - { - 'value': 'Bollini, Andrea', - 'language': null, - 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', - 'confidence': 600, - 'place': 0 - } - ], - 'dc.date.accessioned': [ - { - 'value': '2020-09-05T16:33:33Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.date.available': [ - { - 'value': '2020-09-05T16:33:33Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.abstract': [ - { - 'value': 'DSpace-CRIS is the first free open-source extension of DSpace for the Research Data and Information Management ever developed. Differently from other (commercial) CRIS/RIMS (star), DSpace-CRIS has the institutional repository as its core component, providing high visibility on the web to all the collected information and objects. DSpace-CRIS broadens DSpace functionality and expands its data model while remaining aligned with its code base. \n\nDSpace-CRIS adopts/is compliant with international standards and practices to facilitate interoperability and data transfer:\n\n- ORCID API v2 (complete compliance including pull/push of info for profiles, publications, projects). ORCID API v3 compliance is being released.\n\n- Signposting and ResourceSync (which implement COAR NGR Recommended Behaviors)\n\n- OpenAIRE Guidelines for Literature Repository Managers v4, for Data Archives, for CRIS Managers v1.1.1 (based on CERIF, released Nov. 2019)\n\n- PlanS (by Coalition S)\n\n- FAIR principles\n\nThe main characteristic of DSpace-CRIS is its flexible data model, which allows you to collect and manage research data and information typical of a CRIS system, to define entities and attributes with their reciprocal links. If you would just want to enhance the management of authors, provide name variants and IDs such as the ORCiD, exploit the varied ecosystem of persistent identifiers, link researchers to projects, awards, etc., DSpace-CRIS flexible data model can support this without aggravating the management burden of a normal institutional repository, while giving a great added value. Besides, it has useful features such as the collaboration network graph, aggregated (by researcher, by department) bibliometrics and statistics with graphic reporting, CVs and bibliographies, integration with ORCiD API v.3 and much more, you can explore them vie the menu items here on the left. \n\nIts flexibility allows to configure different data models and metadata schemas, providing the community with new and creative uses of DSpace, such as DSpace-GLAM (Galleries, Libraries, Archives and Museums) for the Cultural Heritage.', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.provenance': [ - { - 'value': 'Made available in DSpace on 2020-09-05T16:33:33Z (GMT). No. of bitstreams: 1\nlayers.png: 295537 bytes, checksum: 001e24b05d2c5d6cd76e88580f10cb9b (MD5)', - 'language': 'en', - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.identifier.uri': [ - { - 'value': 'https://dspacecris7.4science.cloud/handle/123456789/34', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.subject': [ - { - 'value': 'cerif', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - }, - { - 'value': 'datamanagement', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 1 - }, - { - 'value': 'opensource', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 2 - } - ], - 'dc.title': [ - { - 'value': 'DSpace-CRIS', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.type': [ - { - 'value': 'applied research', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.entity.type': [ - { - 'value': 'Project', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.acronym': [ - { - 'value': 'DSC', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.oamandate': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.oamandate.url': [ - { - 'value': 'https://www.4science.it/en/open-source/', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.project.startDate': [ - { - 'value': '2009-04', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.project.status': [ - { - 'value': 'ongoing', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'inArchive': true, - 'discoverable': true, - 'withdrawn': false, - 'lastModified': '2021-08-02T14:19:24.927+00:00', - 'entityType': 'Project', - 'type': 'item', - '_links': { - 'bundles': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/bundles' - }, - 'mappedCollections': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/mappedCollections' - }, - 'owningCollection': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/owningCollection' - }, - 'relationships': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/relationships' - }, - 'version': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/version' - }, - 'templateItemOf': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/templateItemOf' - }, - 'metrics': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/metrics' - }, - 'thumbnail': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/thumbnail' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c' - } - } - } - }, - 'type': 'subscription' - }, - { - 'id': 25, - 'subscriptionType': 'content', - 'subscriptionParameterList': [ - { - 'id': 84, - 'name': 'frequency', - 'value': 'D' - }, - { - 'id': 85, - 'name': 'frequency', - 'value': 'M' - }, - { - 'id': 86, - 'name': 'frequency', - 'value': 'W' - } - ], - '_links': { - 'dSpaceObject': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/25/dSpaceObject' - }, - 'ePerson': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/25/ePerson' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/25' - } - }, - '_embedded': { - 'ePerson': { - 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'name': 'dspacedemo+admin@gmail.com', - 'handle': null, - 'metadata': { - 'dspace.agreements.cookies': [ - { - 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.agreements.end-user': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.firstname': [ - { - 'value': 'Demo', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.lastname': [ - { - 'value': 'Site Administrator', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'netid': null, - 'lastActive': '2021-09-01T12:06:19.000+00:00', - 'canLogIn': true, - 'email': 'dspacedemo+admin@gmail.com', - 'requireCertificate': false, - 'selfRegistered': false, - 'type': 'eperson', - '_links': { - 'groups': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' - } - } - }, - 'dSpaceObject': { - 'id': '90f3b9ce-db65-479c-90d7-8c794abf942c', - 'uuid': '90f3b9ce-db65-479c-90d7-8c794abf942c', - 'name': 'DSpace-CRIS', - 'handle': '123456789/34', - 'metadata': { - 'crispj.coinvestigators': [ - { - 'value': 'Mornati, Susanna', - 'language': null, - 'authority': '1325093a-1ef4-4d87-920d-02ce544fea00', - 'confidence': 600, - 'place': 0 - }, - { - 'value': 'Lombardi, Corrado', - 'language': null, - 'authority': 'b5ad6864-012d-4989-8e0d-4acfa1156fd9', - 'confidence': 600, - 'place': 1 - } - ], - 'crispj.coordinator': [ - { - 'value': '4Science', - 'language': null, - 'authority': 'a14ba215-c0f0-4b74-b21a-06359bfabd45', - 'confidence': 600, - 'place': 0 - } - ], - 'crispj.investigator': [ - { - 'value': 'Bollini, Andrea', - 'language': null, - 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', - 'confidence': 600, - 'place': 0 - } - ], - 'dc.date.accessioned': [ - { - 'value': '2020-09-05T16:33:33Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.date.available': [ - { - 'value': '2020-09-05T16:33:33Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.abstract': [ - { - 'value': 'DSpace-CRIS is the first free open-source extension of DSpace for the Research Data and Information Management ever developed. Differently from other (commercial) CRIS/RIMS (star), DSpace-CRIS has the institutional repository as its core component, providing high visibility on the web to all the collected information and objects. DSpace-CRIS broadens DSpace functionality and expands its data model while remaining aligned with its code base. \n\nDSpace-CRIS adopts/is compliant with international standards and practices to facilitate interoperability and data transfer:\n\n- ORCID API v2 (complete compliance including pull/push of info for profiles, publications, projects). ORCID API v3 compliance is being released.\n\n- Signposting and ResourceSync (which implement COAR NGR Recommended Behaviors)\n\n- OpenAIRE Guidelines for Literature Repository Managers v4, for Data Archives, for CRIS Managers v1.1.1 (based on CERIF, released Nov. 2019)\n\n- PlanS (by Coalition S)\n\n- FAIR principles\n\nThe main characteristic of DSpace-CRIS is its flexible data model, which allows you to collect and manage research data and information typical of a CRIS system, to define entities and attributes with their reciprocal links. If you would just want to enhance the management of authors, provide name variants and IDs such as the ORCiD, exploit the varied ecosystem of persistent identifiers, link researchers to projects, awards, etc., DSpace-CRIS flexible data model can support this without aggravating the management burden of a normal institutional repository, while giving a great added value. Besides, it has useful features such as the collaboration network graph, aggregated (by researcher, by department) bibliometrics and statistics with graphic reporting, CVs and bibliographies, integration with ORCiD API v.3 and much more, you can explore them vie the menu items here on the left. \n\nIts flexibility allows to configure different data models and metadata schemas, providing the community with new and creative uses of DSpace, such as DSpace-GLAM (Galleries, Libraries, Archives and Museums) for the Cultural Heritage.', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.provenance': [ - { - 'value': 'Made available in DSpace on 2020-09-05T16:33:33Z (GMT). No. of bitstreams: 1\nlayers.png: 295537 bytes, checksum: 001e24b05d2c5d6cd76e88580f10cb9b (MD5)', - 'language': 'en', - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.identifier.uri': [ - { - 'value': 'https://dspacecris7.4science.cloud/handle/123456789/34', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.subject': [ - { - 'value': 'cerif', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - }, - { - 'value': 'datamanagement', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 1 - }, - { - 'value': 'opensource', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 2 - } - ], - 'dc.title': [ - { - 'value': 'DSpace-CRIS', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.type': [ - { - 'value': 'applied research', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.entity.type': [ - { - 'value': 'Project', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.acronym': [ - { - 'value': 'DSC', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.oamandate': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.oamandate.url': [ - { - 'value': 'https://www.4science.it/en/open-source/', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.project.startDate': [ - { - 'value': '2009-04', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.project.status': [ - { - 'value': 'ongoing', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'inArchive': true, - 'discoverable': true, - 'withdrawn': false, - 'lastModified': '2021-08-02T14:19:24.927+00:00', - 'entityType': 'Project', - 'type': 'item', - '_links': { - 'bundles': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/bundles' - }, - 'mappedCollections': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/mappedCollections' - }, - 'owningCollection': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/owningCollection' - }, - 'relationships': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/relationships' - }, - 'version': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/version' - }, - 'templateItemOf': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/templateItemOf' - }, - 'metrics': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/metrics' - }, - 'thumbnail': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/thumbnail' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c' - } - } - } - }, - 'type': 'subscription' - }, - { - 'id': 26, - 'subscriptionType': 'content', - 'subscriptionParameterList': [ - { - 'id': 87, - 'name': 'frequency', - 'value': 'D' - }, - { - 'id': 88, - 'name': 'frequency', - 'value': 'M' - } - ], - '_links': { - 'dSpaceObject': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/26/dSpaceObject' - }, - 'ePerson': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/26/ePerson' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/26' - } - }, - '_embedded': { - 'ePerson': { - 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'name': 'dspacedemo+admin@gmail.com', - 'handle': null, - 'metadata': { - 'dspace.agreements.cookies': [ - { - 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.agreements.end-user': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.firstname': [ - { - 'value': 'Demo', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.lastname': [ - { - 'value': 'Site Administrator', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'netid': null, - 'lastActive': '2021-09-01T12:06:19.000+00:00', - 'canLogIn': true, - 'email': 'dspacedemo+admin@gmail.com', - 'requireCertificate': false, - 'selfRegistered': false, - 'type': 'eperson', - '_links': { - 'groups': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' - } - } - }, - 'dSpaceObject': { - 'id': '90f3b9ce-db65-479c-90d7-8c794abf942c', - 'uuid': '90f3b9ce-db65-479c-90d7-8c794abf942c', - 'name': 'DSpace-CRIS', - 'handle': '123456789/34', - 'metadata': { - 'crispj.coinvestigators': [ - { - 'value': 'Mornati, Susanna', - 'language': null, - 'authority': '1325093a-1ef4-4d87-920d-02ce544fea00', - 'confidence': 600, - 'place': 0 - }, - { - 'value': 'Lombardi, Corrado', - 'language': null, - 'authority': 'b5ad6864-012d-4989-8e0d-4acfa1156fd9', - 'confidence': 600, - 'place': 1 - } - ], - 'crispj.coordinator': [ - { - 'value': '4Science', - 'language': null, - 'authority': 'a14ba215-c0f0-4b74-b21a-06359bfabd45', - 'confidence': 600, - 'place': 0 - } - ], - 'crispj.investigator': [ - { - 'value': 'Bollini, Andrea', - 'language': null, - 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', - 'confidence': 600, - 'place': 0 - } - ], - 'dc.date.accessioned': [ - { - 'value': '2020-09-05T16:33:33Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.date.available': [ - { - 'value': '2020-09-05T16:33:33Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.abstract': [ - { - 'value': 'DSpace-CRIS is the first free open-source extension of DSpace for the Research Data and Information Management ever developed. Differently from other (commercial) CRIS/RIMS (star), DSpace-CRIS has the institutional repository as its core component, providing high visibility on the web to all the collected information and objects. DSpace-CRIS broadens DSpace functionality and expands its data model while remaining aligned with its code base. \n\nDSpace-CRIS adopts/is compliant with international standards and practices to facilitate interoperability and data transfer:\n\n- ORCID API v2 (complete compliance including pull/push of info for profiles, publications, projects). ORCID API v3 compliance is being released.\n\n- Signposting and ResourceSync (which implement COAR NGR Recommended Behaviors)\n\n- OpenAIRE Guidelines for Literature Repository Managers v4, for Data Archives, for CRIS Managers v1.1.1 (based on CERIF, released Nov. 2019)\n\n- PlanS (by Coalition S)\n\n- FAIR principles\n\nThe main characteristic of DSpace-CRIS is its flexible data model, which allows you to collect and manage research data and information typical of a CRIS system, to define entities and attributes with their reciprocal links. If you would just want to enhance the management of authors, provide name variants and IDs such as the ORCiD, exploit the varied ecosystem of persistent identifiers, link researchers to projects, awards, etc., DSpace-CRIS flexible data model can support this without aggravating the management burden of a normal institutional repository, while giving a great added value. Besides, it has useful features such as the collaboration network graph, aggregated (by researcher, by department) bibliometrics and statistics with graphic reporting, CVs and bibliographies, integration with ORCiD API v.3 and much more, you can explore them vie the menu items here on the left. \n\nIts flexibility allows to configure different data models and metadata schemas, providing the community with new and creative uses of DSpace, such as DSpace-GLAM (Galleries, Libraries, Archives and Museums) for the Cultural Heritage.', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.provenance': [ - { - 'value': 'Made available in DSpace on 2020-09-05T16:33:33Z (GMT). No. of bitstreams: 1\nlayers.png: 295537 bytes, checksum: 001e24b05d2c5d6cd76e88580f10cb9b (MD5)', - 'language': 'en', - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.identifier.uri': [ - { - 'value': 'https://dspacecris7.4science.cloud/handle/123456789/34', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.subject': [ - { - 'value': 'cerif', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - }, - { - 'value': 'datamanagement', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 1 - }, - { - 'value': 'opensource', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 2 - } - ], - 'dc.title': [ - { - 'value': 'DSpace-CRIS', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.type': [ - { - 'value': 'applied research', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.entity.type': [ - { - 'value': 'Project', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.acronym': [ - { - 'value': 'DSC', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.oamandate': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.oamandate.url': [ - { - 'value': 'https://www.4science.it/en/open-source/', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.project.startDate': [ - { - 'value': '2009-04', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.project.status': [ - { - 'value': 'ongoing', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'inArchive': true, - 'discoverable': true, - 'withdrawn': false, - 'lastModified': '2021-08-02T14:19:24.927+00:00', - 'entityType': 'Project', - 'type': 'item', - '_links': { - 'bundles': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/bundles' - }, - 'mappedCollections': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/mappedCollections' - }, - 'owningCollection': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/owningCollection' - }, - 'relationships': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/relationships' - }, - 'version': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/version' - }, - 'templateItemOf': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/templateItemOf' - }, - 'metrics': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/metrics' - }, - 'thumbnail': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/thumbnail' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c' - } - } - } - }, - 'type': 'subscription' - }, - { - 'id': 27, - 'subscriptionType': 'content', - 'subscriptionParameterList': [ - { - 'id': 89, - 'name': 'frequency', - 'value': 'D' - }, - { - 'id': 90, - 'name': 'frequency', - 'value': 'M' - }, - { - 'id': 91, - 'name': 'frequency', - 'value': 'W' - } - ], - '_links': { - 'dSpaceObject': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/27/dSpaceObject' - }, - 'ePerson': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/27/ePerson' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/27' - } - }, - '_embedded': { - 'ePerson': { - 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'name': 'dspacedemo+admin@gmail.com', - 'handle': null, - 'metadata': { - 'dspace.agreements.cookies': [ - { - 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.agreements.end-user': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.firstname': [ - { - 'value': 'Demo', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.lastname': [ - { - 'value': 'Site Administrator', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'netid': null, - 'lastActive': '2021-09-01T12:06:19.000+00:00', - 'canLogIn': true, - 'email': 'dspacedemo+admin@gmail.com', - 'requireCertificate': false, - 'selfRegistered': false, - 'type': 'eperson', - '_links': { - 'groups': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' - } - } - }, - 'dSpaceObject': { - 'id': '90f3b9ce-db65-479c-90d7-8c794abf942c', - 'uuid': '90f3b9ce-db65-479c-90d7-8c794abf942c', - 'name': 'DSpace-CRIS', - 'handle': '123456789/34', - 'metadata': { - 'crispj.coinvestigators': [ - { - 'value': 'Mornati, Susanna', - 'language': null, - 'authority': '1325093a-1ef4-4d87-920d-02ce544fea00', - 'confidence': 600, - 'place': 0 - }, - { - 'value': 'Lombardi, Corrado', - 'language': null, - 'authority': 'b5ad6864-012d-4989-8e0d-4acfa1156fd9', - 'confidence': 600, - 'place': 1 - } - ], - 'crispj.coordinator': [ - { - 'value': '4Science', - 'language': null, - 'authority': 'a14ba215-c0f0-4b74-b21a-06359bfabd45', - 'confidence': 600, - 'place': 0 - } - ], - 'crispj.investigator': [ - { - 'value': 'Bollini, Andrea', - 'language': null, - 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', - 'confidence': 600, - 'place': 0 - } - ], - 'dc.date.accessioned': [ - { - 'value': '2020-09-05T16:33:33Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.date.available': [ - { - 'value': '2020-09-05T16:33:33Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.abstract': [ - { - 'value': 'DSpace-CRIS is the first free open-source extension of DSpace for the Research Data and Information Management ever developed. Differently from other (commercial) CRIS/RIMS (star), DSpace-CRIS has the institutional repository as its core component, providing high visibility on the web to all the collected information and objects. DSpace-CRIS broadens DSpace functionality and expands its data model while remaining aligned with its code base. \n\nDSpace-CRIS adopts/is compliant with international standards and practices to facilitate interoperability and data transfer:\n\n- ORCID API v2 (complete compliance including pull/push of info for profiles, publications, projects). ORCID API v3 compliance is being released.\n\n- Signposting and ResourceSync (which implement COAR NGR Recommended Behaviors)\n\n- OpenAIRE Guidelines for Literature Repository Managers v4, for Data Archives, for CRIS Managers v1.1.1 (based on CERIF, released Nov. 2019)\n\n- PlanS (by Coalition S)\n\n- FAIR principles\n\nThe main characteristic of DSpace-CRIS is its flexible data model, which allows you to collect and manage research data and information typical of a CRIS system, to define entities and attributes with their reciprocal links. If you would just want to enhance the management of authors, provide name variants and IDs such as the ORCiD, exploit the varied ecosystem of persistent identifiers, link researchers to projects, awards, etc., DSpace-CRIS flexible data model can support this without aggravating the management burden of a normal institutional repository, while giving a great added value. Besides, it has useful features such as the collaboration network graph, aggregated (by researcher, by department) bibliometrics and statistics with graphic reporting, CVs and bibliographies, integration with ORCiD API v.3 and much more, you can explore them vie the menu items here on the left. \n\nIts flexibility allows to configure different data models and metadata schemas, providing the community with new and creative uses of DSpace, such as DSpace-GLAM (Galleries, Libraries, Archives and Museums) for the Cultural Heritage.', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.provenance': [ - { - 'value': 'Made available in DSpace on 2020-09-05T16:33:33Z (GMT). No. of bitstreams: 1\nlayers.png: 295537 bytes, checksum: 001e24b05d2c5d6cd76e88580f10cb9b (MD5)', - 'language': 'en', - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.identifier.uri': [ - { - 'value': 'https://dspacecris7.4science.cloud/handle/123456789/34', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.subject': [ - { - 'value': 'cerif', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - }, - { - 'value': 'datamanagement', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 1 - }, - { - 'value': 'opensource', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 2 - } - ], - 'dc.title': [ - { - 'value': 'DSpace-CRIS', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.type': [ - { - 'value': 'applied research', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.entity.type': [ - { - 'value': 'Project', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.acronym': [ - { - 'value': 'DSC', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.oamandate': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.oamandate.url': [ - { - 'value': 'https://www.4science.it/en/open-source/', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.project.startDate': [ - { - 'value': '2009-04', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.project.status': [ - { - 'value': 'ongoing', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'inArchive': true, - 'discoverable': true, - 'withdrawn': false, - 'lastModified': '2021-08-02T14:19:24.927+00:00', - 'entityType': 'Project', - 'type': 'item', - '_links': { - 'bundles': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/bundles' - }, - 'mappedCollections': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/mappedCollections' - }, - 'owningCollection': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/owningCollection' - }, - 'relationships': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/relationships' - }, - 'version': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/version' - }, - 'templateItemOf': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/templateItemOf' - }, - 'metrics': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/metrics' - }, - 'thumbnail': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c/thumbnail' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/90f3b9ce-db65-479c-90d7-8c794abf942c' - } - } - } - }, - 'type': 'subscription' - }, - { - 'id': 30, - 'subscriptionType': 'statistics', - 'subscriptionParameterList': [ - { - 'id': 96, - 'name': 'frequency', - 'value': 'M' - }, - { - 'id': 97, - 'name': 'frequency', - 'value': 'D' - } - ], - '_links': { - 'dSpaceObject': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/30/dSpaceObject' - }, - 'ePerson': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/30/ePerson' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/30' - } - }, - '_embedded': { - 'ePerson': { - 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'name': 'dspacedemo+admin@gmail.com', - 'handle': null, - 'metadata': { - 'dspace.agreements.cookies': [ - { - 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.agreements.end-user': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.firstname': [ - { - 'value': 'Demo', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.lastname': [ - { - 'value': 'Site Administrator', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'netid': null, - 'lastActive': '2021-09-01T12:06:19.000+00:00', - 'canLogIn': true, - 'email': 'dspacedemo+admin@gmail.com', - 'requireCertificate': false, - 'selfRegistered': false, - 'type': 'eperson', - '_links': { - 'groups': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' - } - } - }, - 'dSpaceObject': { - 'id': 'b2140cd5-bfdf-4b5b-83fb-8bab4c899b40', - 'uuid': 'b2140cd5-bfdf-4b5b-83fb-8bab4c899b40', - 'name': 'DSpace administration issues: the community admin patch', - 'handle': '123456789/107', - 'metadata': { - 'dc.contributor.author': [ - { - 'value': 'Donohue, Tim', - 'language': null, - 'authority': 'fcae3ff0-6a04-4385-8cae-04a38bbe4969', - 'confidence': 600, - 'place': 0 - }, - { - 'value': 'Bollini, Andrea', - 'language': null, - 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', - 'confidence': 600, - 'place': 1 - } - ], - 'dc.date.accessioned': [ - { - 'value': '2020-12-06T22:35:52Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.date.available': [ - { - 'value': '2020-12-06T22:35:52Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.date.issued': [ - { - 'value': '2005', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.abstract': [ - { - 'value': 'A large or medium repository, but also a small repository in some special cases, needs to allow a more decentralized management of administrative activities as: creation of new communities, creation of new collections, management of submitter and workflow groups, editing of published items, access policies and so on. Until now, DSpace allows only a partial decentralization of this functionalities thought into the role of COLLECTION ADMIN. After highlighting these needs, we will introduce the new role of COMMUNITY ADMIN and the changes made to the COLLECTION ADMIN role by our patch so to fix most of the previous needs. We will talk about the \'long history\' of this patch, made for the first time by Andrea against the 1.2 series and next kept updated, bug free and XMLUI aware by Tim from the 1.4 series. This \'pass the buck\', from Andrea to Tim and again together with some other people, shows how useful is for anyone to share results, experiences and customizations with the community so to get them back improved, reducing the cost of locale maintenance, debug and bug fix. We will close the presentation with the good news of the inclusion of this patch in the DSpace codebase for both XMLUI and JSPUI interfaces and also giving some notices about possible improvements in the next future. DSpace User Group Meeting 2009', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.provenance': [ - { - 'value': 'Made available in DSpace on 2020-12-06T22:35:52Z (GMT). No. of bitstreams: 0\n Previous issue date: 2005', - 'language': 'en', - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.identifier.other': [ - { - 'value': 'od______1149::b43204759808a65356e6cc3ba285d29f', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.identifier.uri': [ - { - 'value': 'https://dspacecris7.4science.cloud/handle/123456789/107', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.language.iso': [ - { - 'value': 'en', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.source': [ - { - 'value': 'Göteborgs universitets publikationer - e-publicering och e-arkiv', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.subject': [ - { - 'value': 'dspace', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - }, - { - 'value': 'open source', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 1 - } - ], - 'dc.title': [ - { - 'value': 'DSpace administration issues: the community admin patch', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.type': [ - { - 'value': 'Controlled Vocabulary for Resource Type Genres::text::conference object', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.entity.type': [ - { - 'value': 'Publication', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.author.affiliation': [ - { - 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - }, - { - 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 1 - } - ] - }, - 'inArchive': true, - 'discoverable': true, - 'withdrawn': false, - 'lastModified': '2021-05-31T21:41:41.737+00:00', - 'entityType': 'Publication', - 'type': 'item', - '_links': { - 'bundles': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/bundles' - }, - 'mappedCollections': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/mappedCollections' - }, - 'owningCollection': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/owningCollection' - }, - 'relationships': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/relationships' - }, - 'version': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/version' - }, - 'templateItemOf': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/templateItemOf' - }, - 'metrics': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/metrics' - }, - 'thumbnail': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/thumbnail' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40' - } - } - } - }, - 'type': 'subscription' - }, - { - 'id': 31, - 'subscriptionType': 'content', - 'subscriptionParameterList': [ - { - 'id': 98, - 'name': 'frequency', - 'value': 'D' - }, - { - 'id': 99, - 'name': 'frequency', - 'value': 'M' - } - ], - '_links': { - 'dSpaceObject': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/31/dSpaceObject' - }, - 'ePerson': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/31/ePerson' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/31' - } - }, - '_embedded': { - 'ePerson': { - 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'name': 'dspacedemo+admin@gmail.com', - 'handle': null, - 'metadata': { - 'dspace.agreements.cookies': [ - { - 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.agreements.end-user': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.firstname': [ - { - 'value': 'Demo', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.lastname': [ - { - 'value': 'Site Administrator', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'netid': null, - 'lastActive': '2021-09-01T12:06:19.000+00:00', - 'canLogIn': true, - 'email': 'dspacedemo+admin@gmail.com', - 'requireCertificate': false, - 'selfRegistered': false, - 'type': 'eperson', - '_links': { - 'groups': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' - } - } - }, - 'dSpaceObject': { - 'id': 'b2140cd5-bfdf-4b5b-83fb-8bab4c899b40', - 'uuid': 'b2140cd5-bfdf-4b5b-83fb-8bab4c899b40', - 'name': 'DSpace administration issues: the community admin patch', - 'handle': '123456789/107', - 'metadata': { - 'dc.contributor.author': [ - { - 'value': 'Donohue, Tim', - 'language': null, - 'authority': 'fcae3ff0-6a04-4385-8cae-04a38bbe4969', - 'confidence': 600, - 'place': 0 - }, - { - 'value': 'Bollini, Andrea', - 'language': null, - 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', - 'confidence': 600, - 'place': 1 - } - ], - 'dc.date.accessioned': [ - { - 'value': '2020-12-06T22:35:52Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.date.available': [ - { - 'value': '2020-12-06T22:35:52Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.date.issued': [ - { - 'value': '2005', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.abstract': [ - { - 'value': 'A large or medium repository, but also a small repository in some special cases, needs to allow a more decentralized management of administrative activities as: creation of new communities, creation of new collections, management of submitter and workflow groups, editing of published items, access policies and so on. Until now, DSpace allows only a partial decentralization of this functionalities thought into the role of COLLECTION ADMIN. After highlighting these needs, we will introduce the new role of COMMUNITY ADMIN and the changes made to the COLLECTION ADMIN role by our patch so to fix most of the previous needs. We will talk about the \'long history\' of this patch, made for the first time by Andrea against the 1.2 series and next kept updated, bug free and XMLUI aware by Tim from the 1.4 series. This \'pass the buck\', from Andrea to Tim and again together with some other people, shows how useful is for anyone to share results, experiences and customizations with the community so to get them back improved, reducing the cost of locale maintenance, debug and bug fix. We will close the presentation with the good news of the inclusion of this patch in the DSpace codebase for both XMLUI and JSPUI interfaces and also giving some notices about possible improvements in the next future. DSpace User Group Meeting 2009', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.provenance': [ - { - 'value': 'Made available in DSpace on 2020-12-06T22:35:52Z (GMT). No. of bitstreams: 0\n Previous issue date: 2005', - 'language': 'en', - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.identifier.other': [ - { - 'value': 'od______1149::b43204759808a65356e6cc3ba285d29f', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.identifier.uri': [ - { - 'value': 'https://dspacecris7.4science.cloud/handle/123456789/107', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.language.iso': [ - { - 'value': 'en', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.source': [ - { - 'value': 'Göteborgs universitets publikationer - e-publicering och e-arkiv', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.subject': [ - { - 'value': 'dspace', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - }, - { - 'value': 'open source', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 1 - } - ], - 'dc.title': [ - { - 'value': 'DSpace administration issues: the community admin patch', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.type': [ - { - 'value': 'Controlled Vocabulary for Resource Type Genres::text::conference object', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.entity.type': [ - { - 'value': 'Publication', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.author.affiliation': [ - { - 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - }, - { - 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 1 - } - ] - }, - 'inArchive': true, - 'discoverable': true, - 'withdrawn': false, - 'lastModified': '2021-05-31T21:41:41.737+00:00', - 'entityType': 'Publication', - 'type': 'item', - '_links': { - 'bundles': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/bundles' - }, - 'mappedCollections': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/mappedCollections' - }, - 'owningCollection': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/owningCollection' - }, - 'relationships': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/relationships' - }, - 'version': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/version' - }, - 'templateItemOf': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/templateItemOf' - }, - 'metrics': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/metrics' - }, - 'thumbnail': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/thumbnail' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40' - } - } - } - }, - 'type': 'subscription' - }, - { - 'id': 32, - 'subscriptionType': 'content', - 'subscriptionParameterList': [ - { - 'id': 100, - 'name': 'frequency', - 'value': 'W' - } - ], - '_links': { - 'dSpaceObject': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/32/dSpaceObject' - }, - 'ePerson': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/32/ePerson' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/32' - } - }, - '_embedded': { - 'ePerson': { - 'id': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'uuid': '335647b6-8a52-4ecb-a8c1-7ebabb199bda', - 'name': 'dspacedemo+admin@gmail.com', - 'handle': null, - 'metadata': { - 'dspace.agreements.cookies': [ - { - 'value': '{\'authentication\':true,\'preferences\':true,\'acknowledgement\':true,\'google-analytics\':true}', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.agreements.end-user': [ - { - 'value': 'true', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.firstname': [ - { - 'value': 'Demo', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'eperson.lastname': [ - { - 'value': 'Site Administrator', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ] - }, - 'netid': null, - 'lastActive': '2021-09-01T12:06:19.000+00:00', - 'canLogIn': true, - 'email': 'dspacedemo+admin@gmail.com', - 'requireCertificate': false, - 'selfRegistered': false, - 'type': 'eperson', - '_links': { - 'groups': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda' - } - } - }, - 'dSpaceObject': { - 'id': 'b2140cd5-bfdf-4b5b-83fb-8bab4c899b40', - 'uuid': 'b2140cd5-bfdf-4b5b-83fb-8bab4c899b40', - 'name': 'DSpace administration issues: the community admin patch', - 'handle': '123456789/107', - 'metadata': { - 'dc.contributor.author': [ - { - 'value': 'Donohue, Tim', - 'language': null, - 'authority': 'fcae3ff0-6a04-4385-8cae-04a38bbe4969', - 'confidence': 600, - 'place': 0 - }, - { - 'value': 'Bollini, Andrea', - 'language': null, - 'authority': '092b59e8-8159-4e70-98b5-93ec60bd3431', - 'confidence': 600, - 'place': 1 - } - ], - 'dc.date.accessioned': [ - { - 'value': '2020-12-06T22:35:52Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.date.available': [ - { - 'value': '2020-12-06T22:35:52Z', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.date.issued': [ - { - 'value': '2005', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.abstract': [ - { - 'value': 'A large or medium repository, but also a small repository in some special cases, needs to allow a more decentralized management of administrative activities as: creation of new communities, creation of new collections, management of submitter and workflow groups, editing of published items, access policies and so on. Until now, DSpace allows only a partial decentralization of this functionalities thought into the role of COLLECTION ADMIN. After highlighting these needs, we will introduce the new role of COMMUNITY ADMIN and the changes made to the COLLECTION ADMIN role by our patch so to fix most of the previous needs. We will talk about the \'long history\' of this patch, made for the first time by Andrea against the 1.2 series and next kept updated, bug free and XMLUI aware by Tim from the 1.4 series. This \'pass the buck\', from Andrea to Tim and again together with some other people, shows how useful is for anyone to share results, experiences and customizations with the community so to get them back improved, reducing the cost of locale maintenance, debug and bug fix. We will close the presentation with the good news of the inclusion of this patch in the DSpace codebase for both XMLUI and JSPUI interfaces and also giving some notices about possible improvements in the next future. DSpace User Group Meeting 2009', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.description.provenance': [ - { - 'value': 'Made available in DSpace on 2020-12-06T22:35:52Z (GMT). No. of bitstreams: 0\n Previous issue date: 2005', - 'language': 'en', - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.identifier.other': [ - { - 'value': 'od______1149::b43204759808a65356e6cc3ba285d29f', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.identifier.uri': [ - { - 'value': 'https://dspacecris7.4science.cloud/handle/123456789/107', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.language.iso': [ - { - 'value': 'en', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.source': [ - { - 'value': 'Göteborgs universitets publikationer - e-publicering och e-arkiv', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.subject': [ - { - 'value': 'dspace', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - }, - { - 'value': 'open source', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 1 - } - ], - 'dc.title': [ - { - 'value': 'DSpace administration issues: the community admin patch', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dc.type': [ - { - 'value': 'Controlled Vocabulary for Resource Type Genres::text::conference object', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'dspace.entity.type': [ - { - 'value': 'Publication', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - } - ], - 'oairecerif.author.affiliation': [ - { - 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 0 - }, - { - 'value': '#PLACEHOLDER_PARENT_METADATA_VALUE#', - 'language': null, - 'authority': null, - 'confidence': -1, - 'place': 1 - } - ] - }, - 'inArchive': true, - 'discoverable': true, - 'withdrawn': false, - 'lastModified': '2021-05-31T21:41:41.737+00:00', - 'entityType': 'Publication', - 'type': 'item', - '_links': { - 'bundles': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/bundles' - }, - 'mappedCollections': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/mappedCollections' - }, - 'owningCollection': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/owningCollection' - }, - 'relationships': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/relationships' - }, - 'version': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/version' - }, - 'templateItemOf': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/templateItemOf' - }, - 'metrics': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/metrics' - }, - 'thumbnail': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40/thumbnail' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/items/b2140cd5-bfdf-4b5b-83fb-8bab4c899b40' - } - } - } - }, - 'type': 'subscription' - } +import { EPerson } from '../../core/eperson/models/eperson.model'; +import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; +import { Item } from '../../core/shared/item.model'; +import { ITEM_TYPE } from '../../core/shared/item-relationships/item-type.resource-type'; + +export const mockSubscriptionEperson = Object.assign(new EPerson(), { + 'id': 'fake-eperson-id', + 'uuid': 'fake-eperson-id', + 'handle': null, + 'metadata': { + 'eperson.firstname': [ + { + 'value': 'user', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } + ], + 'eperson.lastname': [ + { + 'value': 'testr', + 'language': null, + 'authority': null, + 'confidence': -1, + 'place': 0 + } ] -}; - -export const findByEPersonAndDsoRes = { - 'type': { - 'value': 'paginated-list' + }, + 'netid': null, + 'lastActive': '2021-09-01T12:06:19.000+00:00', + 'canLogIn': true, + 'email': 'user@test.com', + 'requireCertificate': false, + 'selfRegistered': false, + 'type': 'eperson', + '_links': { + 'groups': { + 'href': 'https://dspace.org/server/api/eperson/epersons/fake-eperson-id/groups' }, - 'pageInfo': { - 'elementsPerPage': 20, - 'totalElements': 2, - 'totalPages': 1, - 'currentPage': 1 - }, - '_links': { - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/search/findByEPersonAndDso?dspace_object_id=092b59e8-8159-4e70-98b5-93ec60bd3431&eperson_id=335647b6-8a52-4ecb-a8c1-7ebabb199bda' - }, - 'page': [ - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22' - }, - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/48' - } - ] - }, - 'page': [ - { - 'id': 22, - 'subscriptionType': 'content', - 'subscriptionParameterList': [ - { - 'id': 161, - 'name': 'frequency', - 'value': 'M' - } - ], - '_links': { - 'dSpaceObject': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22/dSpaceObject' - }, - 'ePerson': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22/ePerson' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22' - } - }, - 'type': 'subscription' - }, - { - 'id': 48, - 'subscriptionType': 'statistics', - 'subscriptionParameterList': [ - { - 'id': 159, - 'name': 'frequency', - 'value': 'D' - }, - { - 'id': 160, - 'name': 'frequency', - 'value': 'M' - } - ], - '_links': { - 'dSpaceObject': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/48/dSpaceObject' - }, - 'ePerson': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/48/ePerson' - }, - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/48' - } - }, - 'type': 'subscription' - } - ] -}; + 'self': { + 'href': 'https://dspace.org/server/api/eperson/epersons/fake-eperson-id' + } + } +}); +export const mockSubscriptionDSO = Object.assign(new Item(), + { + id: 'fake-item-id', + uuid: 'fake-item-id', + metadata: { + 'dc.title': [{ value: 'test item subscription' }] + }, + type: ITEM_TYPE, + _links: { + self: { + href: 'https://dspace.org/server/api/core/items/fake-item-id' + } + } + } +); +export const mockSubscriptionDSO2 = Object.assign(new Item(), + { + id: 'fake-item-id2', + uuid: 'fake-item-id2', + metadata: { + 'dc.title': [{ value: 'test item subscription 2' }] + }, + type: ITEM_TYPE, + _links: { + self: { + href: 'https://dspace.org/server/api/core/items/fake-item-id2' + } + } + } +); export const findByEPersonAndDsoResEmpty = { - 'type': { - 'value': 'paginated-list' + 'type': { + 'value': 'paginated-list' + }, + 'pageInfo': { + 'elementsPerPage': 0, + 'totalElements': 0, + 'totalPages': 1, + 'currentPage': 1 + }, + '_links': { + 'self': { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/search/findByEPersonAndDso?dspace_object_id=092b59e8-8159-4e70-98b5-93ec60bd3431&eperson_id=335647b6-8a52-4ecb-a8c1-7ebabb199bda' }, - 'pageInfo': { - 'elementsPerPage': 0, - 'totalElements': 0, - 'totalPages': 1, - 'currentPage': 1 - }, - '_links': { - 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/search/findByEPersonAndDso?dspace_object_id=092b59e8-8159-4e70-98b5-93ec60bd3431&eperson_id=335647b6-8a52-4ecb-a8c1-7ebabb199bda' - }, - 'page': [ - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22' - }, - { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/48' - } - ] - }, - 'page': [] + 'page': [ + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/22' + }, + { + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/48' + } + ] + }, + 'page': [] }; export const subscriptionMock = { - 'id': 21, - 'type': 'subscription', - 'subscriptionParameterList': [ - { - 'id': 77, - 'name': 'frequency', - 'value': 'D' - }, - { - 'id': 78, - 'name': 'frequency', - 'value': 'M' - } - ], - 'subscriptionType': 'test1', - '_links': { - 'dSpaceObject': { - 'href': 'https://dspace/server/api/core/subscriptions/21/dSpaceObject' - }, - 'ePerson': { - 'href': 'https://dspace/server/api/core/subscriptions/21/ePerson' - }, - 'self': { - 'href': 'https://dspace/server/api/core/subscriptions/21' - } + 'id': 21, + 'type': 'subscription', + 'subscriptionParameterList': [ + { + 'id': 77, + 'name': 'frequency', + 'value': 'D' + }, + { + 'id': 78, + 'name': 'frequency', + 'value': 'M' } + ], + 'subscriptionType': 'test1', + 'ePerson': createSuccessfulRemoteDataObject$(mockSubscriptionEperson), + 'dSpaceObject': createSuccessfulRemoteDataObject$(mockSubscriptionDSO), + '_links': { + 'dSpaceObject': { + 'href': 'https://dspace/server/api/core/subscriptions/21/dSpaceObject' + }, + 'ePerson': { + 'href': 'https://dspace/server/api/core/subscriptions/21/ePerson' + }, + 'self': { + 'href': 'https://dspace/server/api/core/subscriptions/21' + } + } }; export const subscriptionMock2 = { @@ -4475,6 +143,8 @@ export const subscriptionMock2 = { }, ], 'subscriptionType': 'test2', + 'ePerson': createSuccessfulRemoteDataObject$(mockSubscriptionEperson), + 'dSpaceObject': createSuccessfulRemoteDataObject$(mockSubscriptionDSO2), '_links': { 'dSpaceObject': { 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/21/dSpaceObject' diff --git a/src/app/subscriptions-page/subscriptions-page.component.html b/src/app/subscriptions-page/subscriptions-page.component.html index 56e4980ca7..37b882fe44 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.html +++ b/src/app/subscriptions-page/subscriptions-page.component.html @@ -7,14 +7,13 @@ - +
- +
@@ -35,7 +34,7 @@ - + {{ 'subscriptions.table.empty.message' | translate }} diff --git a/src/app/subscriptions-page/subscriptions-page.component.spec.ts b/src/app/subscriptions-page/subscriptions-page.component.spec.ts index 77d2f6805a..9b17d72deb 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.spec.ts +++ b/src/app/subscriptions-page/subscriptions-page.component.spec.ts @@ -1,41 +1,31 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; - - -// Import modules import { CommonModule } from '@angular/common'; import { BrowserModule, By } from '@angular/platform-browser'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { ActivatedRoute } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { of as observableOf } from 'rxjs'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -// Import components import { SubscriptionsPageComponent } from './subscriptions-page.component'; - -// Import services import { PaginationService } from '../core/pagination/pagination.service'; import { SubscriptionService } from '../shared/subscriptions/subscription.service'; import { PaginationServiceStub } from '../shared/testing/pagination-service.stub'; import { AuthService } from '../core/auth/auth.service'; - -// Import utils -import { HostWindowService } from '../shared/host-window.service'; -import { HostWindowServiceStub } from '../shared/testing/host-window-service.stub'; - - -// Import mocks import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; -import { findAllSubscriptionRes } from '../shared/testing/subscriptions-data.mock'; -import { MockActivatedRoute } from '../shared/mocks/active-router.mock'; -import { of as observableOf } from 'rxjs'; -import { EPersonMock } from '../shared/testing/eperson.mock'; -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { VarDirective } from '../shared/utils/var.directive'; import { - SubscriptionViewComponent -} from '../shared/subscriptions/components/subscription-view/subscription-view.component'; - + mockSubscriptionEperson, + subscriptionMock, + subscriptionMock2 +} from '../shared/testing/subscriptions-data.mock'; +import { MockActivatedRoute } from '../shared/mocks/active-router.mock'; +import { VarDirective } from '../shared/utils/var.directive'; +import { SubscriptionViewComponent } from '../shared/subscriptions/subscription-view/subscription-view.component'; +import { PageInfo } from '../core/shared/page-info.model'; +import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; +import { buildPaginatedList } from '../core/data/paginated-list.model'; describe('SubscriptionsPageComponent', () => { let component: SubscriptionsPageComponent; @@ -43,15 +33,26 @@ describe('SubscriptionsPageComponent', () => { let de: DebugElement; const authServiceStub = jasmine.createSpyObj('authorizationService', { - getAuthenticatedUserFromStore: observableOf(EPersonMock) + getAuthenticatedUserFromStore: observableOf(mockSubscriptionEperson) }); const subscriptionServiceStub = jasmine.createSpyObj('SubscriptionService', { - findByEPerson: observableOf(findAllSubscriptionRes) + findByEPerson: jasmine.createSpy('findByEPerson') }); + const paginationService = new PaginationServiceStub(); - beforeEach(waitForAsync( () => { + const mockSubscriptionList = [subscriptionMock, subscriptionMock2]; + + const emptyPageInfo = Object.assign(new PageInfo(), { + totalElements: 0 + }); + + const pageInfo = Object.assign(new PageInfo(), { + totalElements: 2 + }); + + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ CommonModule, @@ -65,63 +66,62 @@ describe('SubscriptionsPageComponent', () => { }), NoopAnimationsModule ], - declarations: [ SubscriptionsPageComponent, SubscriptionViewComponent, VarDirective ], - providers:[ + declarations: [SubscriptionsPageComponent, SubscriptionViewComponent, VarDirective], + providers: [ { provide: SubscriptionService, useValue: subscriptionServiceStub }, - { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: ActivatedRoute, useValue: new MockActivatedRoute() }, { provide: AuthService, useValue: authServiceStub }, { provide: PaginationService, useValue: paginationService } ], schemas: [NO_ERRORS_SCHEMA] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(SubscriptionsPageComponent); component = fixture.componentInstance; de = fixture.debugElement; - fixture.detectChanges(); }); - it('should create', () => { - fixture.detectChanges(); - expect(component).toBeTruthy(); - }); + describe('when there are subscriptions', () => { - describe('when table', () => { - - it('should show table', async() => { - await fixture.whenStable(); + beforeEach(() => { + subscriptionServiceStub.findByEPerson.and.returnValue(createSuccessfulRemoteDataObject$(buildPaginatedList(pageInfo, mockSubscriptionList))); fixture.detectChanges(); - const table = de.query(By.css('table')); - expect(table).toBeTruthy(); }); + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should show table', () => { + expect(de.query(By.css('[data-test="subscription-table"]'))).toBeTruthy(); + expect(de.query(By.css('[data-test="empty-alert"]'))).toBeNull(); + }); + + it('should show a row for each results entry',() => { + expect(de.query(By.css('[data-test="subscription-table"]'))).toBeTruthy(); + expect(de.query(By.css('[data-test="empty-alert"]'))).toBeNull(); + expect(de.queryAll(By.css('tbody > tr')).length).toEqual(2); + }); }); - it('should show all the results', () => { - expect(de.queryAll(By.css('tbody > tr')).length).toEqual(10); - }); + describe('when there are no subscriptions', () => { - it('should have dso object info', () => { - expect(de.query(By.css('.dso-info > span'))).toBeTruthy(); - expect(de.query(By.css('.dso-info > p > a'))).toBeTruthy(); - }); + beforeEach(() => { + subscriptionServiceStub.findByEPerson.and.returnValue(createSuccessfulRemoteDataObject$(buildPaginatedList(emptyPageInfo, []))); + fixture.detectChanges(); + }); - it('should have subscription type info', () => { - expect(de.query(By.css('.subscription-type'))).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should have subscription paramenter info', () => { - expect(de.query(By.css('.subscription-parmenters > span'))).toBeTruthy(); + it('should not show table', () => { + expect(de.query(By.css('[data-test="subscription-table"]'))).toBeNull(); + expect(de.query(By.css('[data-test="empty-alert"]'))).toBeTruthy(); + }); }); - it('should have subscription action info', () => { - expect(de.query(By.css('.btn-outline-primary'))).toBeTruthy(); - expect(de.query(By.css('.btn-outline-danger'))).toBeTruthy(); - }); - - }); diff --git a/src/app/subscriptions-page/subscriptions-page.component.ts b/src/app/subscriptions-page/subscriptions-page.component.ts index a99daa9d68..2eed8b5fa5 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.ts +++ b/src/app/subscriptions-page/subscriptions-page.component.ts @@ -11,7 +11,8 @@ import { PaginationService } from '../core/pagination/pagination.service'; import { PageInfo } from '../core/shared/page-info.model'; import { AuthService } from '../core/auth/auth.service'; import { EPerson } from '../core/eperson/models/eperson.model'; -import { getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; +import { getFirstCompletedRemoteData } from '../core/shared/operators'; +import { RemoteData } from '../core/data/remote-data'; @Component({ selector: 'ds-subscriptions-page', @@ -43,7 +44,7 @@ export class SubscriptionsPageComponent implements OnInit { ePersonId$: Observable; /** - * EPerson id of the logged in user + * EPerson id of the logged-in user */ // ePersonId: string; @@ -61,35 +62,26 @@ export class SubscriptionsPageComponent implements OnInit { this.ePersonId$ = this.authService.getAuthenticatedUserFromStore().pipe( take(1), map((ePerson: EPerson) => ePerson.id), - shareReplay(), - /*tap((ePersonId: string) => { // TODO unused - this.ePersonId = ePersonId; - }),*/ + shareReplay() ); this.retrieveSubscriptions(); } private retrieveSubscriptions() { this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( - tap(console.log), combineLatestWith(this.ePersonId$), - tap(() => {this.loading$.next(true);}), + tap(() => this.loading$.next(true)), switchMap(([currentPagination, ePersonId]) => this.subscriptionService.findByEPerson(ePersonId,{ currentPage: currentPagination.currentPage, elementsPerPage: currentPagination.pageSize })), - getFirstSucceededRemoteDataPayload(), - tap((x) => console.log('find', x)), - // getFirstSucceededRemoteDataPayload(), - ).subscribe({ - next: (res: any) => { - console.log('next',res); - this.subscriptions$.next(res); - this.loading$.next(false); - }, - error: () => { - this.loading$.next(false); + getFirstCompletedRemoteData() + + ).subscribe((res: RemoteData>) => { + if (res.hasSucceeded) { + this.subscriptions$.next(res.payload); } + this.loading$.next(false); }); } /** From 27d5097f4b24795224579dd2bc4e08585ee28390 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 28 Dec 2022 16:22:17 +0100 Subject: [PATCH 08/29] [CST-7757] Add subscribe button to all dso pages --- .../collection-page.component.html | 1 + .../community-page.component.html | 1 + .../journal-issue/journal-issue.component.html | 1 + .../journal-volume.component.html | 1 + .../item-pages/journal/journal.component.html | 1 + .../org-unit/org-unit.component.html | 1 + .../item-pages/person/person.component.html | 1 + .../item-pages/project/project.component.html | 1 + .../full/full-item-page.component.html | 1 + .../untyped-item/untyped-item.component.html | 1 + .../dso-page-subscription-button.component.ts | 18 ++++++++++-------- 11 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/app/collection-page/collection-page.component.html b/src/app/collection-page/collection-page.component.html index eebfdbd829..b236365df5 100644 --- a/src/app/collection-page/collection-page.component.html +++ b/src/app/collection-page/collection-page.component.html @@ -35,6 +35,7 @@
+
diff --git a/src/app/community-page/community-page.component.html b/src/app/community-page/community-page.component.html index 368fec08a5..1b88877f8b 100644 --- a/src/app/community-page/community-page.component.html +++ b/src/app/community-page/community-page.component.html @@ -22,6 +22,7 @@
+
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html index bf73a62447..e12b8a5ceb 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html @@ -6,6 +6,7 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> +
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html index b370431a27..1bc9ec1137 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html @@ -6,6 +6,7 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> +
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html index 27ee373237..b787e937d6 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html @@ -6,6 +6,7 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> +
diff --git a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html index ac92e4ad6d..524adae4ce 100644 --- a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html +++ b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html @@ -6,6 +6,7 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> +
diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.html b/src/app/entity-groups/research-entities/item-pages/person/person.component.html index a73236006b..998c07777f 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.html +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.html @@ -7,6 +7,7 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> +
diff --git a/src/app/entity-groups/research-entities/item-pages/project/project.component.html b/src/app/entity-groups/research-entities/item-pages/project/project.component.html index 3fb5e2d520..b52f5aa8dd 100644 --- a/src/app/entity-groups/research-entities/item-pages/project/project.component.html +++ b/src/app/entity-groups/research-entities/item-pages/project/project.component.html @@ -6,6 +6,7 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> +
diff --git a/src/app/item-page/full/full-item-page.component.html b/src/app/item-page/full/full-item-page.component.html index ee4ead2835..60aa6d6f86 100644 --- a/src/app/item-page/full/full-item-page.component.html +++ b/src/app/item-page/full/full-item-page.component.html @@ -10,6 +10,7 @@
+
diff --git a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.ts b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.ts index 39e685b465..f97439c1a1 100644 --- a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.ts +++ b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.ts @@ -1,8 +1,10 @@ import { Component, Input, OnInit } from '@angular/core'; + import { Observable, of } from 'rxjs'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; + import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { SubscriptionModalComponent } from '../../subscriptions/subscription-modal/subscription-modal.component'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; @@ -23,16 +25,10 @@ export class DsoPageSubscriptionButtonComponent implements OnInit { */ public modalRef: NgbModalRef; - /** - * EPerson id of the logged user - */ - ePersonId: string; - /** * DSpaceObject that is being viewed */ - @Input() - dso: DSpaceObject; + @Input() dso: DSpaceObject; constructor( protected authorizationService: AuthorizationDataService, @@ -40,10 +36,16 @@ export class DsoPageSubscriptionButtonComponent implements OnInit { ) { } + /** + * check if the current DSpaceObject can be subscribed by the user + */ ngOnInit(): void { this.isAuthorized$ = this.authorizationService.isAuthorized(FeatureID.CanSubscribe, this.dso.self); } + /** + * Open the modal to subscribe to the related DSpaceObject + */ public openSubscriptionModal() { this.modalRef = this.modalService.open(SubscriptionModalComponent); this.modalRef.componentInstance.dso = this.dso; From 90a923889510b3f304ac04d5c5231f351ad3fcd8 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 28 Dec 2022 16:23:08 +0100 Subject: [PATCH 09/29] [CST-7757] fix pagination --- .../subscription-modal.component.html | 26 ++++------ .../subscriptions-page.component.html | 4 +- .../subscriptions-page.component.ts | 51 +++++++++++++------ 3 files changed, 48 insertions(+), 33 deletions(-) diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html index ef9e26032d..f78567ee7b 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html @@ -6,15 +6,11 @@
- diff --git a/src/app/subscriptions-page/subscriptions-page.component.html b/src/app/subscriptions-page/subscriptions-page.component.html index 37b882fe44..a327ec658c 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.html +++ b/src/app/subscriptions-page/subscriptions-page.component.html @@ -9,14 +9,14 @@
{{'subscriptions.table.dso' | translate}}
- + diff --git a/src/app/subscriptions-page/subscriptions-page.component.ts b/src/app/subscriptions-page/subscriptions-page.component.ts index 2eed8b5fa5..242d5145ac 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.ts +++ b/src/app/subscriptions-page/subscriptions-page.component.ts @@ -1,6 +1,6 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; -import { BehaviorSubject, combineLatestWith, Observable, shareReplay } from 'rxjs'; +import { BehaviorSubject, combineLatestWith, Observable, shareReplay, Subscription as rxjsSubscription } from 'rxjs'; import { map, switchMap, take, tap } from 'rxjs/operators'; import { Subscription } from '../shared/subscriptions/models/subscription.model'; @@ -11,15 +11,16 @@ import { PaginationService } from '../core/pagination/pagination.service'; import { PageInfo } from '../core/shared/page-info.model'; import { AuthService } from '../core/auth/auth.service'; import { EPerson } from '../core/eperson/models/eperson.model'; -import { getFirstCompletedRemoteData } from '../core/shared/operators'; +import { getAllCompletedRemoteData } from '../core/shared/operators'; import { RemoteData } from '../core/data/remote-data'; +import { hasValue } from '../shared/empty.util'; @Component({ selector: 'ds-subscriptions-page', templateUrl: './subscriptions-page.component.html', styleUrls: ['./subscriptions-page.component.scss'] }) -export class SubscriptionsPageComponent implements OnInit { +export class SubscriptionsPageComponent implements OnInit, OnDestroy { /** * The subscriptions to show on this page, as an Observable list. @@ -27,8 +28,7 @@ export class SubscriptionsPageComponent implements OnInit { subscriptions$: BehaviorSubject> = new BehaviorSubject(buildPaginatedList(new PageInfo(), [])); /** - * The current pagination configuration for the page used by the FindAll method - * Currently simply renders subscriptions + * The current pagination configuration for the page */ config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { id: 'elp', @@ -41,22 +41,26 @@ export class SubscriptionsPageComponent implements OnInit { */ loading$: BehaviorSubject = new BehaviorSubject(false); + /** + * The current eperson id + */ ePersonId$: Observable; /** - * EPerson id of the logged-in user + * The rxjs subscription used to retrieve the result list */ - // ePersonId: string; + sub: rxjsSubscription = null; constructor( private paginationService: PaginationService, private authService: AuthService, private subscriptionService: SubscriptionService - ) { } + ) { + + } /** - * Subscribe the pagination service to send a request with specific pagination - * When page is changed it will request the new subscriptions for the new page config + * Retrieve the current eperson id and call method to retrieve the subscriptions */ ngOnInit(): void { this.ePersonId$ = this.authService.getAuthenticatedUserFromStore().pipe( @@ -67,16 +71,21 @@ export class SubscriptionsPageComponent implements OnInit { this.retrieveSubscriptions(); } - private retrieveSubscriptions() { - this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( + /** + * Retrieve subscription list related to the current user. + * When page is changed it will request the new subscriptions for the new page config + * @private + */ + private retrieveSubscriptions(): void { + this.sub = this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( + tap(console.log), combineLatestWith(this.ePersonId$), tap(() => this.loading$.next(true)), switchMap(([currentPagination, ePersonId]) => this.subscriptionService.findByEPerson(ePersonId,{ currentPage: currentPagination.currentPage, elementsPerPage: currentPagination.pageSize })), - getFirstCompletedRemoteData() - + getAllCompletedRemoteData() ).subscribe((res: RemoteData>) => { if (res.hasSucceeded) { this.subscriptions$.next(res.payload); @@ -85,10 +94,20 @@ export class SubscriptionsPageComponent implements OnInit { }); } /** - * When an action is made and the information is changed refresh the information + * When a subscription is deleted refresh the subscription list */ refresh(): void { + if (hasValue(this.sub)) { + this.sub.unsubscribe(); + } + this.retrieveSubscriptions(); } + ngOnDestroy(): void { + if (hasValue(this.sub)) { + this.sub.unsubscribe(); + } + } + } From 710b11655d99ce72a47fe2328c2c716a201d27e1 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 29 Dec 2022 18:46:19 +0100 Subject: [PATCH 10/29] [CST-7757] Rename type property to subscriptionType --- .../subscription-modal.component.spec.ts | 6 +++--- .../subscription-modal/subscription-modal.component.ts | 8 ++++---- .../subscription-view/subscription-view.component.spec.ts | 6 +++--- .../subscription-view/subscription-view.component.ts | 4 ++-- ...scription.service.ts => subscriptions-data.service.ts} | 4 ++-- .../subscriptions-page.component.spec.ts | 6 +++--- .../subscriptions-page/subscriptions-page.component.ts | 5 ++--- 7 files changed, 19 insertions(+), 20 deletions(-) rename src/app/shared/subscriptions/{subscription.service.ts => subscriptions-data.service.ts} (97%) diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.spec.ts b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.spec.ts index 05fa1c4a77..221f70f77d 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.spec.ts +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.spec.ts @@ -8,7 +8,7 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { SubscriptionModalComponent } from './subscription-modal.component'; import { TranslateLoaderMock } from '../../mocks/translate-loader.mock'; import { NotificationsService } from '../../notifications/notifications.service'; -import { SubscriptionService } from '../subscription.service'; +import { SubscriptionsDataService } from '../subscriptions-data.service'; import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; import { Item } from '../../../core/shared/item.model'; import { AuthService } from '../../../core/auth/auth.service'; @@ -67,7 +67,7 @@ describe('SubscriptionModalComponent', () => { getAuthenticatedUserFromStore: createSuccessfulRemoteDataObject$(mockEperson) }); - subscriptionServiceStub = jasmine.createSpyObj('SubscriptionService', { + subscriptionServiceStub = jasmine.createSpyObj('SubscriptionsDataService', { getSubscriptionsByPersonDSO: jasmine.createSpy('getSubscriptionsByPersonDSO'), createSubscription: createSuccessfulRemoteDataObject$({}), updateSubscription: createSuccessfulRemoteDataObject$({}), @@ -92,7 +92,7 @@ describe('SubscriptionModalComponent', () => { NgbActiveModal, { provide: AuthService, useValue: authService }, { provide: NotificationsService, useValue: notificationServiceStub }, - { provide: SubscriptionService, useValue: subscriptionServiceStub }, + { provide: SubscriptionsDataService, useValue: subscriptionServiceStub }, ], schemas: [ NO_ERRORS_SCHEMA diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts index 23faf33c54..c7ff9882ac 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts @@ -10,7 +10,7 @@ import findIndex from 'lodash/findIndex'; import { Subscription } from '../models/subscription.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; -import { SubscriptionService } from '../subscription.service'; +import { SubscriptionsDataService } from '../subscriptions-data.service'; import { NotificationsService } from '../../notifications/notifications.service'; import { PaginatedList } from '../../../core/data/paginated-list.model'; import { RemoteData } from '../../../core/data/remote-data'; @@ -59,7 +59,7 @@ export class SubscriptionModalComponent implements OnInit { /** * Types of subscription to be shown on select */ - private subscriptionDefaultTypes = ['content', 'statistics']; + private subscriptionDefaultTypes = ['content']; /** * Frequencies to be shown as checkboxes @@ -75,7 +75,7 @@ export class SubscriptionModalComponent implements OnInit { private formBuilder: FormBuilder, private modalService: NgbModal, private notificationsService: NotificationsService, - private subscriptionService: SubscriptionService, + private subscriptionService: SubscriptionsDataService, public activeModal: NgbActiveModal, private authService: AuthService, private translate: TranslateService, @@ -239,7 +239,7 @@ export class SubscriptionModalComponent implements OnInit { private createBody(subscriptionId: string, subscriptionType: string, frequencies: FormGroup): Partial { const body = { id: (isNotEmpty(subscriptionId) ? subscriptionId : null), - type: subscriptionType, + subscriptionType: subscriptionType, subscriptionParameterList: [] }; diff --git a/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts b/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts index 3ec826d6c5..a0caba97ee 100644 --- a/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts +++ b/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts @@ -19,7 +19,7 @@ import { findByEPersonAndDsoResEmpty, subscriptionMock } from '../../testing/sub // Import utils import { NotificationsService } from '../../notifications/notifications.service'; import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; -import { SubscriptionService } from '../subscription.service'; +import { SubscriptionsDataService } from '../subscriptions-data.service'; import { Subscription } from '../models/subscription.model'; import { of as observableOf } from 'rxjs'; @@ -34,7 +34,7 @@ describe('SubscriptionViewComponent', () => { let de: DebugElement; let modalService; - const subscriptionServiceStub = jasmine.createSpyObj('SubscriptionService', { + const subscriptionServiceStub = jasmine.createSpyObj('SubscriptionsDataService', { getSubscriptionByPersonDSO: observableOf(findByEPersonAndDsoResEmpty), deleteSubscription: createSuccessfulRemoteDataObject$({}), updateSubscription: createSuccessfulRemoteDataObject$({}), @@ -73,7 +73,7 @@ describe('SubscriptionViewComponent', () => { providers: [ { provide: ComponentFixtureAutoDetect, useValue: true }, { provide: NotificationsService, useValue: NotificationsServiceStub }, - { provide: SubscriptionService, useValue: subscriptionServiceStub }, + { provide: SubscriptionsDataService, useValue: subscriptionServiceStub }, ] }) .compileComponents(); diff --git a/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts b/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts index b8aa4d2375..9f7fca7caf 100644 --- a/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts +++ b/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts @@ -7,7 +7,7 @@ import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { hasValue } from '../../empty.util'; import { ConfirmationModalComponent } from '../../confirmation-modal/confirmation-modal.component'; -import { SubscriptionService } from '../subscription.service'; +import { SubscriptionsDataService } from '../subscriptions-data.service'; import { getCommunityModuleRoute } from '../../../community-page/community-page-routing-paths'; import { getCollectionModuleRoute } from '../../../collection-page/collection-page-routing-paths'; import { getItemModuleRoute } from '../../../item-page/item-page-routing-paths'; @@ -48,7 +48,7 @@ export class SubscriptionViewComponent { constructor( private modalService: NgbModal, - private subscriptionService: SubscriptionService, + private subscriptionService: SubscriptionsDataService, ) { } /** diff --git a/src/app/shared/subscriptions/subscription.service.ts b/src/app/shared/subscriptions/subscriptions-data.service.ts similarity index 97% rename from src/app/shared/subscriptions/subscription.service.ts rename to src/app/shared/subscriptions/subscriptions-data.service.ts index 480598a15a..9576d98536 100644 --- a/src/app/shared/subscriptions/subscription.service.ts +++ b/src/app/shared/subscriptions/subscriptions-data.service.ts @@ -41,7 +41,7 @@ import { followLink } from '../utils/follow-link-config.model'; providedIn: 'root' }) @dataService(SUBSCRIPTION) -export class SubscriptionService extends IdentifiableDataService { +export class SubscriptionsDataService extends IdentifiableDataService { protected findByEpersonLinkPath = 'findByEPerson'; private deleteData: DeleteDataImpl; @@ -89,7 +89,7 @@ export class SubscriptionService extends IdentifiableDataService { * @param ePerson The ePerson to create for * @param uuid The uuid of the dsobjcet to create for */ - createSubscription(subscription, ePerson: string, uuid: string): Observable> { + createSubscription(subscription: Subscription, ePerson: string, uuid: string): Observable> { return this.halService.getEndpoint(this.linkPath).pipe( isNotEmptyOperator(), diff --git a/src/app/subscriptions-page/subscriptions-page.component.spec.ts b/src/app/subscriptions-page/subscriptions-page.component.spec.ts index 9b17d72deb..4f44392428 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.spec.ts +++ b/src/app/subscriptions-page/subscriptions-page.component.spec.ts @@ -11,7 +11,7 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { SubscriptionsPageComponent } from './subscriptions-page.component'; import { PaginationService } from '../core/pagination/pagination.service'; -import { SubscriptionService } from '../shared/subscriptions/subscription.service'; +import { SubscriptionsDataService } from '../shared/subscriptions/subscriptions-data.service'; import { PaginationServiceStub } from '../shared/testing/pagination-service.stub'; import { AuthService } from '../core/auth/auth.service'; import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; @@ -36,7 +36,7 @@ describe('SubscriptionsPageComponent', () => { getAuthenticatedUserFromStore: observableOf(mockSubscriptionEperson) }); - const subscriptionServiceStub = jasmine.createSpyObj('SubscriptionService', { + const subscriptionServiceStub = jasmine.createSpyObj('SubscriptionsDataService', { findByEPerson: jasmine.createSpy('findByEPerson') }); @@ -68,7 +68,7 @@ describe('SubscriptionsPageComponent', () => { ], declarations: [SubscriptionsPageComponent, SubscriptionViewComponent, VarDirective], providers: [ - { provide: SubscriptionService, useValue: subscriptionServiceStub }, + { provide: SubscriptionsDataService, useValue: subscriptionServiceStub }, { provide: ActivatedRoute, useValue: new MockActivatedRoute() }, { provide: AuthService, useValue: authServiceStub }, { provide: PaginationService, useValue: paginationService } diff --git a/src/app/subscriptions-page/subscriptions-page.component.ts b/src/app/subscriptions-page/subscriptions-page.component.ts index 242d5145ac..7307218d0a 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.ts +++ b/src/app/subscriptions-page/subscriptions-page.component.ts @@ -5,7 +5,7 @@ import { map, switchMap, take, tap } from 'rxjs/operators'; import { Subscription } from '../shared/subscriptions/models/subscription.model'; import { buildPaginatedList, PaginatedList } from '../core/data/paginated-list.model'; -import { SubscriptionService } from '../shared/subscriptions/subscription.service'; +import { SubscriptionsDataService } from '../shared/subscriptions/subscriptions-data.service'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; import { PaginationService } from '../core/pagination/pagination.service'; import { PageInfo } from '../core/shared/page-info.model'; @@ -54,7 +54,7 @@ export class SubscriptionsPageComponent implements OnInit, OnDestroy { constructor( private paginationService: PaginationService, private authService: AuthService, - private subscriptionService: SubscriptionService + private subscriptionService: SubscriptionsDataService ) { } @@ -78,7 +78,6 @@ export class SubscriptionsPageComponent implements OnInit, OnDestroy { */ private retrieveSubscriptions(): void { this.sub = this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( - tap(console.log), combineLatestWith(this.ePersonId$), tap(() => this.loading$.next(true)), switchMap(([currentPagination, ePersonId]) => this.subscriptionService.findByEPerson(ePersonId,{ From 28351f2ac7c8df83cecab87fdd2a834574beded5 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 16 Jan 2023 09:55:56 +0100 Subject: [PATCH 11/29] [CST-7757] Fix merge with main --- src/app/shared/shared.module.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 5162270b14..9a6c855208 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -244,6 +244,9 @@ import { import { ThemedCollectionDropdownComponent } from './collection-dropdown/themed-collection-dropdown.component'; import { MetadataFieldWrapperComponent } from './metadata-field-wrapper/metadata-field-wrapper.component'; import { LogInExternalProviderComponent } from './log-in/methods/log-in-external-provider/log-in-external-provider.component'; +import { + DsoPageSubscriptionButtonComponent +} from './dso-page/dso-page-subscription-button/dso-page-subscription-button.component'; const MODULES = [ CommonModule, @@ -344,6 +347,7 @@ const COMPONENTS = [ ThemedSearchNavbarComponent, ListableNotificationObjectComponent, DsoPageEditButtonComponent, + DsoPageSubscriptionButtonComponent, MetadataFieldWrapperComponent, ]; From 8eab0f743f3769c97e9aebaefe78f5df94cefa90 Mon Sep 17 00:00:00 2001 From: cris Date: Sat, 28 Jan 2023 17:48:39 +0000 Subject: [PATCH 12/29] The collection is displayed in the workflow task --- ...-search-result-list-element.component.html | 1 + .../themed-item-list-preview.component.html | 16 +++++++ .../themed-item-list-preview.component.ts | 47 +++++++++++++++++-- ...-search-result-list-element.component.html | 1 + 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.html diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.html index 5e98b00926..4584b12550 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.html @@ -2,6 +2,7 @@
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.html b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.html new file mode 100644 index 0000000000..a5e4138a5f --- /dev/null +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.html @@ -0,0 +1,16 @@ +
+
+
+
+ + {{"collection.listelement.badge" | translate}}: + +   + + {{collection.metadata["dc.title"][0]["value"]}} + +
+
+
+ +
\ No newline at end of file diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts index 3fe825d236..4d81ff2fbb 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts @@ -1,9 +1,13 @@ -import { Component, Input } from '@angular/core'; +import { ChangeDetectorRef, Component, ComponentFactoryResolver, Input } from '@angular/core'; import { ThemedComponent } from '../../../theme-support/themed.component'; import { ItemListPreviewComponent } from './item-list-preview.component'; import { Item } from '../../../../core/shared/item.model'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { SearchResult } from '../../../search/models/search-result.model'; +import { WorkflowItem } from 'src/app/core/submission/models/workflowitem.model'; +import { ThemeService } from 'src/app/shared/theme-support/theme.service'; +import { CollectionDataService } from 'src/app/core/data/collection-data.service'; +import { environment } from '../../../../../../src/environments/environment'; /** * Themed wrapper for ItemListPreviewComponent @@ -11,7 +15,7 @@ import { SearchResult } from '../../../search/models/search-result.model'; @Component({ selector: 'ds-themed-item-list-preview', styleUrls: [], - templateUrl: '../../../theme-support/themed.component.html', + templateUrl: 'themed-item-list-preview.component.html' }) export class ThemedItemListPreviewComponent extends ThemedComponent { protected inAndOutputNames: (keyof ItemListPreviewComponent & keyof this)[] = ['item', 'object', 'status', 'showSubmitter']; @@ -24,6 +28,43 @@ export class ThemedItemListPreviewComponent extends ThemedComponent { + this.collection = collection?.payload; + console.log("collection", this.collection) + }); + } protected getComponentName(): string { return 'ItemListPreviewComponent'; @@ -36,4 +77,4 @@ export class ThemedItemListPreviewComponent extends ThemedComponent { return import('./item-list-preview.component'); } -} +} \ No newline at end of file diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.html index 4f0d6f774a..d863371446 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.html @@ -2,6 +2,7 @@
From 328b16c1e4d88c3ede9e6f1488f9ea9c47aa3f34 Mon Sep 17 00:00:00 2001 From: cris Date: Sat, 28 Jan 2023 17:58:51 +0000 Subject: [PATCH 13/29] formatting issues fixed --- .../themed-item-list-preview.component.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts index 4d81ff2fbb..54371a8ac8 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts @@ -39,13 +39,13 @@ export class ThemedItemListPreviewComponent extends ThemedComponent { this.collection = collection?.payload; - console.log("collection", this.collection) }); } @@ -77,4 +76,4 @@ export class ThemedItemListPreviewComponent extends ThemedComponent { return import('./item-list-preview.component'); } -} \ No newline at end of file +} From b09de30272215b1e37d2dfefc10f805d53327bfd Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Tue, 31 Jan 2023 10:20:19 +0100 Subject: [PATCH 14/29] [CST-7757] renamed param --- src/app/shared/subscriptions/subscriptions-data.service.ts | 6 +++--- src/app/shared/testing/subscriptions-data.mock.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/shared/subscriptions/subscriptions-data.service.ts b/src/app/shared/subscriptions/subscriptions-data.service.ts index 9576d98536..cf1e90ef96 100644 --- a/src/app/shared/subscriptions/subscriptions-data.service.ts +++ b/src/app/shared/subscriptions/subscriptions-data.service.ts @@ -74,7 +74,7 @@ export class SubscriptionsDataService extends IdentifiableDataService `${endpointUrl}?dspace_object_id=${uuid}&eperson_id=${ePerson}`), + map((endpointUrl: string) => `${endpointUrl}?resource=${uuid}&eperson_id=${ePerson}`), map((endpointURL: string) => new CreateRequest(this.requestService.generateRequestId(), endpointURL, JSON.stringify(subscription))), sendRequest(this.requestService), switchMap((restRequest: RestRequest) => this.rdbService.buildFromRequestUUID(restRequest.uuid)), @@ -114,7 +114,7 @@ export class SubscriptionsDataService extends IdentifiableDataService `${endpointUrl}/${subscription.id}?dspace_object_id=${uuid}&eperson_id=${ePerson}`), + map((endpointUrl: string) => `${endpointUrl}/${subscription.id}?resource=${uuid}&eperson_id=${ePerson}`), map((endpointURL: string) => new PutRequest(this.requestService.generateRequestId(), endpointURL, JSON.stringify(subscription))), sendRequest(this.requestService), switchMap((restRequest: RestRequest) => this.rdbService.buildFromRequestUUID(restRequest.uuid)), diff --git a/src/app/shared/testing/subscriptions-data.mock.ts b/src/app/shared/testing/subscriptions-data.mock.ts index 8a4afc6d52..07108ad516 100644 --- a/src/app/shared/testing/subscriptions-data.mock.ts +++ b/src/app/shared/testing/subscriptions-data.mock.ts @@ -87,7 +87,7 @@ export const findByEPersonAndDsoResEmpty = { }, '_links': { 'self': { - 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/search/findByEPersonAndDso?dspace_object_id=092b59e8-8159-4e70-98b5-93ec60bd3431&eperson_id=335647b6-8a52-4ecb-a8c1-7ebabb199bda' + 'href': 'https://dspacecris7.4science.cloud/server/api/core/subscriptions/search/findByEPersonAndDso?resource=092b59e8-8159-4e70-98b5-93ec60bd3431&eperson_id=335647b6-8a52-4ecb-a8c1-7ebabb199bda' }, 'page': [ { From ac7b5f841e20df0ac7d60e171745b937bf20426c Mon Sep 17 00:00:00 2001 From: cris Date: Sat, 4 Feb 2023 21:28:49 +0000 Subject: [PATCH 15/29] collection is displayed below the submitter --- .../my-dspace-page/my-dspace-search.module.ts | 2 + .../item-collection.component.html | 7 ++ .../item-collection.component.scss | 0 .../item-collection.component.ts | 65 +++++++++++++++++++ .../item-submitter.component.html | 2 +- .../item-list-preview.component.html | 36 +++++----- .../item-list-preview.component.ts | 6 ++ .../themed-item-list-preview.component.html | 16 ----- .../themed-item-list-preview.component.ts | 29 +-------- 9 files changed, 102 insertions(+), 61 deletions(-) create mode 100644 src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.html create mode 100644 src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.scss create mode 100644 src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.ts delete mode 100644 src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.html diff --git a/src/app/my-dspace-page/my-dspace-search.module.ts b/src/app/my-dspace-page/my-dspace-search.module.ts index 1ce39991b3..6e450e8df1 100644 --- a/src/app/my-dspace-page/my-dspace-search.module.ts +++ b/src/app/my-dspace-page/my-dspace-search.module.ts @@ -18,6 +18,7 @@ import { ClaimedApprovedSearchResultListElementComponent } from '../shared/objec import { ClaimedDeclinedSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component'; import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module'; import { ItemSubmitterComponent } from '../shared/object-collection/shared/mydspace-item-submitter/item-submitter.component'; +import { ItemCollectionComponent } from '../shared/object-collection/shared/mydspace-item-collection/item-collection.component'; import { ItemDetailPreviewComponent } from '../shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component'; import { ItemDetailPreviewFieldComponent } from '../shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component'; import { ItemListPreviewComponent } from '../shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component'; @@ -44,6 +45,7 @@ const ENTRY_COMPONENTS = [ const DECLARATIONS = [ ...ENTRY_COMPONENTS, ItemSubmitterComponent, + ItemCollectionComponent, ItemDetailPreviewComponent, ItemDetailPreviewFieldComponent, ItemListPreviewComponent, diff --git a/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.html b/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.html new file mode 100644 index 0000000000..e17ba92a05 --- /dev/null +++ b/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.html @@ -0,0 +1,7 @@ +
+ {{'collection.listelement.badge' | translate}}: + + {{(collection$ | async)?.name}} + + +
\ No newline at end of file diff --git a/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.scss b/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.ts b/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.ts new file mode 100644 index 0000000000..8f857540d4 --- /dev/null +++ b/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.ts @@ -0,0 +1,65 @@ +import { Component, Input, OnInit } from '@angular/core'; + +import { EMPTY, Observable } from 'rxjs'; +import { map, mergeMap } from 'rxjs/operators'; + +import { RemoteData } from '../../../../core/data/remote-data'; +import { isNotEmpty } from '../../../empty.util'; +import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; +import { Collection } from '../../../../core/shared/collection.model'; +import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; +import { LinkService } from '../../../../core/cache/builders/link.service'; +import { followLink } from '../../../utils/follow-link-config.model'; + +/** + * This component represents a badge with collection information. + */ +@Component({ + selector: 'ds-item-collection', + styleUrls: ['./item-collection.component.scss'], + templateUrl: './item-collection.component.html' +}) +export class ItemCollectionComponent implements OnInit { + + /** + * The target object + */ + @Input() object: any; + + /** + * The collection object + */ + collection$: Observable; + + public constructor(protected linkService: LinkService) { + + } + + /** + * Initialize collection object + */ + ngOnInit() { + + this.linkService.resolveLinks(this.object, followLink('workflowitem', {}, + followLink('collection',{}) + )); + this.collection$ = (this.object.workflowitem as Observable>).pipe( + getFirstCompletedRemoteData(), + mergeMap((rd: RemoteData) => { + if (rd.hasSucceeded && isNotEmpty(rd.payload)) { + return (rd.payload.collection as Observable>).pipe( + getFirstCompletedRemoteData(), + map((rds: RemoteData) => { + if (rds.hasSucceeded && isNotEmpty(rds.payload)) { + return rds.payload; + } else { + return null; + } + }) + ); + } else { + return EMPTY; + } + })); + } +} diff --git a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html index 0f7ae433fa..db38f98b04 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html +++ b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html @@ -1,3 +1,3 @@
- {{'submission.workflow.tasks.generic.submitter' | translate}} : {{(submitter$ | async)?.name}} + {{'submission.workflow.tasks.generic.submitter' | translate}}: {{(submitter$ | async)?.name}}
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html index d2db0ba209..94426136b5 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html @@ -15,34 +15,36 @@

- - - ( - + + ( + ) - - {{'mydspace.results.no-authors' | translate}} - - - ; + {{'mydspace.results.no-authors' + | translate}} + + + ; + + + - - - -
+
-
+ \ No newline at end of file diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts index 6b40678ded..39f83bc371 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts @@ -8,6 +8,7 @@ import { import { SearchResult } from '../../../search/models/search-result.model'; import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interface'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { WorkflowItem } from 'src/app/core/submission/models/workflowitem.model'; /** * This component show metadata for the given item object in the list view. @@ -40,6 +41,11 @@ export class ItemListPreviewComponent implements OnInit { */ @Input() showSubmitter = false; + /** + * Represents the workflow of the item + */ + @Input() workflowItem: WorkflowItem; + /** * Display thumbnails if required by configuration */ diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.html b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.html deleted file mode 100644 index a5e4138a5f..0000000000 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.html +++ /dev/null @@ -1,16 +0,0 @@ -
-
-
-
- - {{"collection.listelement.badge" | translate}}: - -   - - {{collection.metadata["dc.title"][0]["value"]}} - -
-
-
- -
\ No newline at end of file diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts index 54371a8ac8..ea5a38e3cb 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts @@ -6,8 +6,6 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa import { SearchResult } from '../../../search/models/search-result.model'; import { WorkflowItem } from 'src/app/core/submission/models/workflowitem.model'; import { ThemeService } from 'src/app/shared/theme-support/theme.service'; -import { CollectionDataService } from 'src/app/core/data/collection-data.service'; -import { environment } from '../../../../../../src/environments/environment'; /** * Themed wrapper for ItemListPreviewComponent @@ -15,10 +13,10 @@ import { environment } from '../../../../../../src/environments/environment'; @Component({ selector: 'ds-themed-item-list-preview', styleUrls: [], - templateUrl: 'themed-item-list-preview.component.html' + templateUrl: '../../../theme-support/themed.component.html' }) export class ThemedItemListPreviewComponent extends ThemedComponent { - protected inAndOutputNames: (keyof ItemListPreviewComponent & keyof this)[] = ['item', 'object', 'status', 'showSubmitter']; + protected inAndOutputNames: (keyof ItemListPreviewComponent & keyof this)[] = ['item', 'object', 'status', 'showSubmitter', 'workflowItem']; @Input() item: Item; @@ -30,39 +28,16 @@ export class ThemedItemListPreviewComponent extends ThemedComponent { - this.collection = collection?.payload; - }); } protected getComponentName(): string { From b1011edb3d18d540e639b30e92bde8753048d900 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 3 Feb 2023 12:29:03 +0100 Subject: [PATCH 16/29] [CST-7757] Remove subscription button from item pages --- .../item-pages/journal-issue/journal-issue.component.html | 1 - .../item-pages/journal-volume/journal-volume.component.html | 1 - .../journal-entities/item-pages/journal/journal.component.html | 1 - .../item-pages/org-unit/org-unit.component.html | 1 - .../research-entities/item-pages/person/person.component.html | 1 - .../research-entities/item-pages/project/project.component.html | 1 - src/app/item-page/full/full-item-page.component.html | 1 - .../simple/item-types/publication/publication.component.html | 1 - .../simple/item-types/untyped-item/untyped-item.component.html | 1 - 9 files changed, 9 deletions(-) diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html index e12b8a5ceb..bf73a62447 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html @@ -6,7 +6,6 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> -
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html index 1bc9ec1137..b370431a27 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html @@ -6,7 +6,6 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> -
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html index b787e937d6..27ee373237 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html @@ -6,7 +6,6 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> -
diff --git a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html index 524adae4ce..ac92e4ad6d 100644 --- a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html +++ b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html @@ -6,7 +6,6 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> -
diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.html b/src/app/entity-groups/research-entities/item-pages/person/person.component.html index 998c07777f..a73236006b 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.html +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.html @@ -7,7 +7,6 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> -
diff --git a/src/app/entity-groups/research-entities/item-pages/project/project.component.html b/src/app/entity-groups/research-entities/item-pages/project/project.component.html index b52f5aa8dd..3fb5e2d520 100644 --- a/src/app/entity-groups/research-entities/item-pages/project/project.component.html +++ b/src/app/entity-groups/research-entities/item-pages/project/project.component.html @@ -6,7 +6,6 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> -
diff --git a/src/app/item-page/full/full-item-page.component.html b/src/app/item-page/full/full-item-page.component.html index 60aa6d6f86..ee4ead2835 100644 --- a/src/app/item-page/full/full-item-page.component.html +++ b/src/app/item-page/full/full-item-page.component.html @@ -10,7 +10,6 @@
-
diff --git a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html index 806bd4baaf..5c3e5e77b0 100644 --- a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html +++ b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html @@ -15,7 +15,6 @@ [tooltipMsgCreate]="'item.page.version.create'" [tooltipMsgHasDraft]="'item.page.version.hasDraft'"> -
From a4d94b0399ae28562bd6e0692770231504089e8c Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 3 Feb 2023 23:37:58 +0100 Subject: [PATCH 17/29] [CST-7757] Response parameters fixed; submit button disabled if frequency is missing --- .../subscriptions/models/subscription.model.ts | 8 ++++---- .../subscription-modal.component.html | 2 +- .../subscription-modal.component.ts | 12 ++++++++++++ .../subscriptions/subscriptions-data.service.ts | 4 ++-- .../subscriptions-page.component.html | 4 ++-- 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/app/shared/subscriptions/models/subscription.model.ts b/src/app/shared/subscriptions/models/subscription.model.ts index 36e43ce996..b460a0418f 100644 --- a/src/app/shared/subscriptions/models/subscription.model.ts +++ b/src/app/shared/subscriptions/models/subscription.model.ts @@ -39,8 +39,8 @@ export class Subscription extends DSpaceObject { @deserialize _links: { self: HALLink; - ePerson: HALLink; - dSpaceObject: HALLink; + eperson: HALLink; + resource: HALLink; }; /** @@ -48,14 +48,14 @@ export class Subscription extends DSpaceObject { * Will be undefined unless the logo {@link HALLink} has been resolved. */ @link(EPERSON) - ePerson?: Observable>; + eperson?: Observable>; /** * The logo for this Community * Will be undefined unless the logo {@link HALLink} has been resolved. */ @link(DSPACE_OBJECT) - dSpaceObject?: Observable>; + resource?: Observable>; /** * The embedded ePerson & dSpaceObject for this Subscription */ diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html index f78567ee7b..156b8717f9 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html @@ -30,7 +30,7 @@ (click)="activeModal.close()"> {{'item.page.subscriptions.modal.close' | translate}} -
From 0f8d2d7b16afbe503141e5747814f17711b2e752 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Fri, 3 Feb 2023 23:45:59 +0100 Subject: [PATCH 18/29] [CST-7757] D-W-M fixed --- .../subscription-modal/subscription-modal.component.ts | 2 +- src/assets/i18n/en.json5 | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts index fa7aec9273..fc7e021b86 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts @@ -64,7 +64,7 @@ export class SubscriptionModalComponent implements OnInit { /** * Frequencies to be shown as checkboxes */ - private frequencyDefaultValues = ['D', 'M', 'W']; + private frequencyDefaultValues = ['D', 'W', 'M']; /** * True if form status has changed and at least one frequency is checked diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 49e0657877..e3fee7198c 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2313,11 +2313,11 @@ "item.page.subscriptions.modal.new-subscription-form.type.content": "Content", - "item.page.subscriptions.modal.new-subscription-form.frequency.D": "daily", + "item.page.subscriptions.modal.new-subscription-form.frequency.D": "Daily", - "item.page.subscriptions.modal.new-subscription-form.frequency.M": "monthly", + "item.page.subscriptions.modal.new-subscription-form.frequency.W": "Weekly", - "item.page.subscriptions.modal.new-subscription-form.frequency.W": "weekly", + "item.page.subscriptions.modal.new-subscription-form.frequency.M": "Monthly", "item.page.subscriptions.modal.new-subscription-form.submit": "Submit", From 0dfb37d15403acd954278e799f1a72a42b9dcfdd Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Mon, 6 Feb 2023 10:22:12 +0100 Subject: [PATCH 19/29] [CST-7757] Show object name --- .../subscription-modal/subscription-modal.component.html | 1 + src/assets/i18n/en.json5 | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html index 156b8717f9..debc6edf50 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html @@ -6,6 +6,7 @@ - - + diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index ba0200a892..27f461e7ea 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4621,6 +4621,14 @@ "subscriptions.table.action": "Action", + "subscriptions.table.edit": "Edit", + + "subscriptions.table.delete": "Delete", + + "subscriptions.table.not-available": "Not available", + + "subscriptions.table.not-available-message": "The subscribed item has been deleted, or you don't currently have the permission to view it", + "subscriptions.table.empty.message": "You do not have any subscriptions at this time. To subscribe to email updates for a Community or Collection, use the subscription button on the object's page.", From 851fe7869ed5c47089e0430e82542c4d08a89477 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Sat, 11 Feb 2023 03:29:17 +0100 Subject: [PATCH 27/29] [CST-7757] Missing label --- src/assets/i18n/en.json5 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index bf32aebe47..fa6f2694fa 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4748,6 +4748,8 @@ "subscriptions.modal.new-subscription-form.submit": "Submit", + "subscriptions.modal.new-subscription-form.processing": "Processing...", + "subscriptions.modal.create.success": "Subscribed to {{ type }} successfully.", "subscriptions.modal.delete.success": "Subscription deleted successfully", From c74ef49797cb0fe9e816cb9648a7503b7094304f Mon Sep 17 00:00:00 2001 From: cris Date: Sat, 11 Feb 2023 17:13:52 +0000 Subject: [PATCH 28/29] fixed issue in browser console --- .../mydspace-item-collection/item-collection.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.ts b/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.ts index 8f857540d4..2f2a20ede3 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.ts +++ b/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.ts @@ -40,7 +40,9 @@ export class ItemCollectionComponent implements OnInit { */ ngOnInit() { - this.linkService.resolveLinks(this.object, followLink('workflowitem', {}, + this.linkService.resolveLinks(this.object, followLink('workflowitem', { + isOptional: true + }, followLink('collection',{}) )); this.collection$ = (this.object.workflowitem as Observable>).pipe( From 078bdd287fbeca3912cc894f2356241925172081 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Mon, 13 Feb 2023 15:45:56 +0100 Subject: [PATCH 29/29] [CST-7757] Fix delete message --- .../subscription-modal.component.html | 2 +- .../subscription-modal.component.ts | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html index 0e723a8140..a71498f002 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html @@ -27,7 +27,7 @@ -

{{'subscriptions.modal.delete-info' | translate}}

+

{{'subscriptions.modal.delete-info' | translate}}

{{'subscriptions.table.dso' | translate}}{{'subscriptions.table.dso' | translate}} {{'subscriptions.table.subscription_type' | translate}} {{'subscriptions.table.subscription_frequency' | translate}} {{'subscriptions.table.action' | translate}}
{{subscription.subscriptionType}} + {{ 'subscriptions.frequency.'+ parameterList.value | translate}}, diff --git a/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts b/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts index a0caba97ee..00f0b8d22c 100644 --- a/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts +++ b/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts @@ -103,7 +103,7 @@ describe('SubscriptionViewComponent', () => { }); it('should have subscription paramenter info', () => { - expect(de.query(By.css('.subscription-parmenters > span'))).toBeTruthy(); + expect(de.query(By.css('.subscription-parameters > span'))).toBeTruthy(); }); it('should have subscription action info', () => { diff --git a/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts b/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts index 9f7fca7caf..4c6c5c6d21 100644 --- a/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts +++ b/src/app/shared/subscriptions/subscription-view/subscription-view.component.ts @@ -19,6 +19,9 @@ import { SubscriptionModalComponent } from '../subscription-modal/subscription-m templateUrl: './subscription-view.component.html', styleUrls: ['./subscription-view.component.scss'] }) +/** + * Table row representing a subscription that displays all information and action buttons to manage it + */ export class SubscriptionViewComponent { /** diff --git a/src/app/subscriptions-page/subscriptions-page.component.ts b/src/app/subscriptions-page/subscriptions-page.component.ts index 7307218d0a..05c587ba12 100644 --- a/src/app/subscriptions-page/subscriptions-page.component.ts +++ b/src/app/subscriptions-page/subscriptions-page.component.ts @@ -20,6 +20,9 @@ import { hasValue } from '../shared/empty.util'; templateUrl: './subscriptions-page.component.html', styleUrls: ['./subscriptions-page.component.scss'] }) +/** + * List and allow to manage all the active subscription for the current user + */ export class SubscriptionsPageComponent implements OnInit, OnDestroy { /** From 7a46e6d93cff1b44961c05f8b2832188604cdec5 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Mon, 6 Feb 2023 11:21:12 +0100 Subject: [PATCH 21/29] [CST-7757] Labels renamed --- ...so-page-subscription-button.component.html | 6 ++-- .../subscription-modal.component.html | 12 +++---- src/assets/i18n/en.json5 | 34 +++++++++---------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html index 8601dcdda9..15135009fc 100644 --- a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html +++ b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html @@ -1,8 +1,8 @@ diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html index debc6edf50..e095e094dc 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html @@ -1,6 +1,6 @@
+ +

{{dso.name}}

+
{{subscription.subscriptionType}} diff --git a/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts b/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts index 00f0b8d22c..aa40f2bf43 100644 --- a/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts +++ b/src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts @@ -94,7 +94,7 @@ describe('SubscriptionViewComponent', () => { }); it('should have dso object info', () => { - expect(de.query(By.css('.dso-info > span'))).toBeTruthy(); + expect(de.query(By.css('.dso-info > ds-type-badge'))).toBeTruthy(); expect(de.query(By.css('.dso-info > p > a'))).toBeTruthy(); }); diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index be9ffc99ed..ba0200a892 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4589,6 +4589,8 @@ "subscriptions.modal.close": "Close", + "subscriptions.modal.delete-info": "To remove this subscription, please visit the \"Subscriptions\" page under your user profile", + "subscriptions.modal.new-subscription-form.type.content": "Content", "subscriptions.modal.new-subscription-form.frequency.D": "Daily", @@ -4619,8 +4621,7 @@ "subscriptions.table.action": "Action", - "subscriptions.table.empty.message": "You have not subscribed any notification yet. To subscribe notification about an object please use the contextual menu in the object detail view", - + "subscriptions.table.empty.message": "You do not have any subscriptions at this time. To subscribe to email updates for a Community or Collection, use the subscription button on the object's page.", "thumbnail.default.alt": "Thumbnail Image", From 516b91f520f273bcfee62be6cd095bd27c6d3060 Mon Sep 17 00:00:00 2001 From: Davide Negretti Date: Thu, 9 Feb 2023 12:06:15 +0100 Subject: [PATCH 26/29] [CST-7757] Hide delete info for new subscriptions and fix issue with unavailable items --- .../subscription-modal.component.html | 2 +- .../subscription-modal.component.ts | 4 ++ .../subscription-view.component.html | 42 ++++++++++++------- src/assets/i18n/en.json5 | 8 ++++ 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html index 5cbfb138d4..0e723a8140 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html @@ -27,7 +27,7 @@ -

{{'subscriptions.modal.delete-info' | translate}}

+

{{'subscriptions.modal.delete-info' | translate}}

- {{subscription.subscriptionType}} + + +

{{dso.name}}

+
+ +

{{ 'subscriptions.table.not-available' | translate }}

+

{{ 'subscriptions.table.not-available-message' | translate }}

+
+
+ {{subscription.subscriptionType}} - + - {{ 'subscriptions.frequency.'+ parameterList.value | translate}}, + {{ 'subscriptions.frequency.' + parameterList.value | translate}}, - + -
- - -
+
+ + +