Production release February 2024 [CONNECT] #34

Merged
konstantina.galouni merged 168 commits from develop into master 2024-02-15 11:04:20 +01:00
106 changed files with 3183 additions and 1389 deletions

View File

@ -21,14 +21,38 @@ export class SearchOrcidService {
let url = properties.searchOrcidURL + term + '/record';
return this.http.get(url, { headers: headers })
//.map(res => res.json()['person'])
.pipe(map(res => res['person']))
.pipe(map(res => [res['name']['given-names'],
res['name']['family-name'],
res['name']]))
.pipe(map(res => SearchOrcidService.parseOrcidAuthor(res, authorIds, authors, addId)));
}
//.map(res => res.json()['person'])
.pipe(map(res => res['person']))
.pipe(map(res => [res['name']['given-names'],
res['name']['family-name'],
res['name'], res['name']['institution-name']]))
.pipe(map(res => SearchOrcidService.parseOrcidAuthor(res, authorIds, authors, addId)));
}
searchOrcidSingleAuthor(term: string, properties: EnvProperties, addId): any {
//var headers = new Headers();
//headers.append('Accept', 'application/orcid+json');
let headers = new HttpHeaders({'Accept': 'application/orcid+json'});
let url ="https://pub.orcid.org/v3.0/expanded-search/?q=orcid:" + term + '&start=0&rows=50';
return this.http.get(url, {headers: headers})
.pipe(map(res => res['expanded-result']))
.pipe(map(res => {
if(res) {
for (let auth_result of res) {
const author = {};
author['id'] = auth_result['orcid-id'];
author['authorGivenName'] = auth_result['given-names'];
author['authorFamilyName'] = auth_result['family-names'];
author['institutions'] = auth_result['institution-name'];
return author;
}
}
return null;
}));
}
searchOrcidAuthors(term: string,
properties: EnvProperties): any {
@ -45,17 +69,46 @@ export class SearchOrcidService {
}
searchOrcidAuthorsNew(term: string,
properties: EnvProperties, size = 10): any {
let headers = new HttpHeaders({'Accept': 'application/orcid+json'});
// let url = properties.searchOrcidURL+'search?defType=edismax&q='+term+'&qf=given-name^1.0+family-name^2.0+other-names^1.0+credit-name^1.0&start=0&rows=10';
let url = /*properties.searchOrcidURL +*/ 'https://pub.orcid.org/v3.0/expanded-search?q=' + StringUtils.URIEncode('{!edismax qf="given-and-family-names^50.0 family-name^10.0 given-names^10.0 credit-name^10.0 other-names^5.0 text^1.0" pf="given-and-family-names^50.0" bq="current-institution-affiliation-name:[* TO *]^100.0 past-institution-affiliation-name:[* TO *]^70" mm=1}') + term + '&start=0&rows=' + size;
// given-and-family-names^50.0 family-name^10.0 given-names^10.0 credit-name^10.0 other-names^5.0 text^1.0" pf="given-and-family-names^50.0" bq="current-institution-affiliation-name:[* TO *]^100.0 past-institution-affiliation-name:[* TO *]^70" mm=1}
// https://pub.orcid.org/v3.0/expanded-search/?q=%7B!edismax%20qf%3D%22given-and-family-names%5E50.0%20family-name%5E10.0%20given-names%5E10.0%20credit-name%5E10.0%20other-names%5E5.0%20text%5E1.0%22%20pf%3D%22given-and-family-names%5E50.0%22%20bq%3D%22current-institution-affiliation-name%3A%5B*%20TO%20*%5D%5E100.0%20past-institution-affiliation-name%3A%5B*%20TO%20*%5D%5E70%22%20mm%3D1%7Dpaolo%20manghi&start=0&rows=50
//q={!edismax qf="given-and-family-names^50.0 family-name^10.0 given-names^5.0 credit-name^10.0 other-names^5.0 text^1.0" pf="given-and-family-names^50.0" mm=1}alessia bardi&start=0&rows=10
let key = url;
return this.http.get(url, {headers: headers})
.pipe(map(res => res['expanded-result']))
.pipe(map(res => {
let authors = [];
if(res) {
for (let auth_result of res) {
const author = {};
author['id'] = auth_result['orcid-id'];
author['authorGivenName'] = auth_result['given-names'];
author['authorFamilyName'] = auth_result['family-names'];
author['institutions'] = auth_result['institution-name'];
authors.push(author);
}
}
return authors;
}));
}
searchOrcidPublications(id: string, properties: EnvProperties, parse: boolean = false): any {
let headers = new HttpHeaders({'Accept': 'application/orcid+json'});
let url = properties.searchOrcidURL + id + '/works';
return this.http.get(url, { headers: headers })
.pipe(map(res => res['group']))
.pipe(map(request => (parse ? SearchOrcidService.parse(id, request) : request)));
}
return this.http.get(url, { headers: headers })
.pipe(map(res => res['group']))
.pipe(map(request => (parse ? SearchOrcidService.parse(id, request) : request)));
}
static parseOrcidAuthor(data: any, authorIds: string[], authors, addId): any {
console.log(data)
if (data[2] != null) {
if (addId) {
authorIds.push(data[2].path);
@ -72,7 +125,12 @@ export class SearchOrcidService {
} else {
author['authorFamilyName'] = "";
}
if (data[3] != null) {
author['institution'] = data[3];
}
console.log(author['institution'])
authors.push(author);
return true;
}
return false;

View File

@ -0,0 +1,19 @@
import { NgModule} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import {SearchOrcidService} from "./searchOrcid.service";
@NgModule({
imports: [
CommonModule, FormsModule
],
declarations: [
],
providers:[
SearchOrcidService
],
exports: [
]
})
export class SearchOrcidServiceModule { }

View File

@ -37,7 +37,6 @@ export class ClaimsAdminComponent {
@Input() isConnect: boolean = false;
@Input() externalPortalUrl: string;
@Input() claimsInfoURL: string;
@Input() userInfoURL: string;
public user: User = null;
sub;

View File

@ -29,7 +29,7 @@ export class DirectLinkingComponent {
// linkTo: string = null; // entity type (project or context or entity)
// linkToEntities: string[] = [];
showOptions:ShowOptions = new ShowOptions();
validEntityTypes = ["dataset", "publication", "software", "orp", "project", "context"];
validEntityTypes = ["dataset", "publication", "software", "other", "project", "context"];
sources: ClaimEntity[] = [];
inlineEntity: ClaimEntity = null;
validInput: boolean = null;//'true;
@ -74,7 +74,7 @@ export class DirectLinkingComponent {
this.getResearchResultById("dataset", this.id);
} else if (this.type == "software") {
this.getResearchResultById("software", this.id);
} else if (this.type == "orp") {
} else if (this.type == "other") {
this.getResearchResultById("other", this.id);
} else {
this.validInput = this.isValidInput(null);
@ -110,9 +110,9 @@ export class DirectLinkingComponent {
return false;
} else if (this.type == "project" && this.showOptions.linkTo != "result") {
return false;
} else if (["dataset", "publication", "software", "orp"].indexOf(this.type) != -1 && (["project", "context", "result"].indexOf(this.showOptions.linkTo) == -1)) {
} else if (["dataset", "publication", "software", "other"].indexOf(this.type) != -1 && (["project", "context", "result"].indexOf(this.showOptions.linkTo) == -1)) {
return false;
} else if (["project", "dataset", "publication", "software", "orp"].indexOf(this.type) == -1) {
} else if (["project", "dataset", "publication", "software", "other"].indexOf(this.type) == -1) {
return false;
} else {
return true;

View File

@ -16,6 +16,7 @@ import {
import {UserManagementService} from "../../../services/user-management.service";
import {Subscriber, timer} from "rxjs";
import {map} from "rxjs/operators";
import {LogService} from "../../../utils/log/log.service";
@Component({
selector: 'claim-insert',
@ -59,7 +60,7 @@ import {map} from "rxjs/operators";
})
export class ClaimInsertComponent {
constructor(private claimService: ClaimsService, private _router: Router, private route: ActivatedRoute,
private userManagementService: UserManagementService) {
private userManagementService: UserManagementService, private _logService: LogService) {
}
subscriptions = [];
ngOnDestroy() {
@ -127,6 +128,7 @@ export class ClaimInsertComponent {
public feedRecordsJob;
public claims2Insert;
public records2Insert
infoToLog = [];
public insert() {
this.confirmOpen();
}
@ -145,6 +147,7 @@ export class ClaimInsertComponent {
this.errorInClaims = [];
this.insertedRecords = [];
this.errorInRecords = [];
this.infoToLog = [];
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
if (!user) {
this.saveAndNavigate();
@ -190,10 +193,11 @@ export class ClaimInsertComponent {
} else if (entity.project) {
claims.push(ClaimInsertComponent.createProjectClaim(result, entity, user.email, dashboard));
}
this.infoToLog.push([ result.title?result.title: result.id, entity.title?entity.title:entity.id]);
}
if (this.inlineEntity) {
this.infoToLog.push([ this.inlineEntity.title?this.inlineEntity.title: this.inlineEntity.id, entity.title?entity.title:entity.id]);
if (this.inlineEntity.result) {
if (entity.result) {
@ -256,6 +260,12 @@ export class ClaimInsertComponent {
data => {
this.claims2Insert = claims.length;
this.claimsJob = data.data;
if(this.properties.logServiceUrl) {
for(let info of this.infoToLog) {
this.subscriptions.push(this._logService.logLink(this.properties, info[0],info[1]).subscribe(res => {
}));
}
}
this.saveLocalStorage();
let timerSubscription = timer(0, 10000).pipe(
map(() => {
@ -375,7 +385,7 @@ export class ClaimInsertComponent {
localStorage.removeItem(this.localStoragePrefix + "claimsJob");
localStorage.removeItem(this.localStoragePrefix + "feedRecordsJob");
this._router.navigate(['/myclaims'], {queryParams: this.params});
this._router.navigate([this.properties.myClaimsLink], {queryParams: this.params});
}
}

View File

@ -6,10 +6,11 @@ import {LoadingModalModule} from '../../../utils/modal/loadingModal.module';
import {ClaimInsertComponent} from './insertClaim.component';
import {ClaimServiceModule} from '../../claim-utils/service/claimsService.module';
import {IconsModule} from "../../../utils/icons/icons.module";
import {LogServiceModule} from "../../../utils/log/LogService.module";
@NgModule({
imports: [
SharedModule, AlertModalModule, LoadingModalModule, ClaimServiceModule, IconsModule
SharedModule, AlertModalModule, LoadingModalModule, ClaimServiceModule, IconsModule, LogServiceModule
],
declarations: [ClaimInsertComponent],
exports:[ ClaimInsertComponent]

View File

@ -30,7 +30,6 @@ import {Subscriber} from "rxjs";
export class MyClaimsComponent {
@Input() claimsInfoURL: string;
@Input() communityId:string;
@Input() userInfoURL: string;
public user: User = null;
constructor(private userManagementService: UserManagementService, private _router: Router) {}

View File

@ -6,6 +6,9 @@ import {CommunityInfo} from "./community/communityInfo";
export class ConnectHelper {
public static getCommunityFromDomain(domain: string): string{
if(properties.dashboard === 'irish') {
return properties.adminToolsCommunity;
}
if(properties.environment == "development" &&
(properties.adminToolsPortalType == "connect" || properties.adminToolsPortalType == "community"
|| properties.adminToolsPortalType == "aggregator" || properties.adminToolsPortalType == "eosc")) {

View File

@ -1,17 +1,18 @@
import {Option} from "../../sharedComponents/input/input.component";
import {StakeholderEntities} from "../../monitor/entities/stakeholder";
import {StakeholderConfiguration} from "../../monitor-admin/utils/indicator-utils";
export class PortalUtils{
export class PortalUtils {
portalTypes: Option[] = [
{value: 'explore', label: 'Explore Portal'},
{value: 'connect', label: 'Connect portal'},
{value: 'monitor', label: 'Monitor portal'},
{value: 'community', label: 'Community Gateway'},
{value: 'funder', label: StakeholderEntities.FUNDER + ' Dashboard'},
{value: 'ri', label: StakeholderEntities.RI + ' Dashboard'},
{value: 'organization', label: StakeholderEntities.ORGANIZATION + ' Dashboard'},
{value: 'project', label: StakeholderEntities.PROJECT + ' Dashboard'}
{value: 'funder', label: StakeholderConfiguration.ENTITIES.funder + ' ' + StakeholderConfiguration.ENTITIES.stakeholder},
{value: 'ri', label: StakeholderConfiguration.ENTITIES.ri + ' ' + StakeholderConfiguration.ENTITIES.stakeholder},
{value: 'organization', label: StakeholderConfiguration.ENTITIES.organization + ' ' + StakeholderConfiguration.ENTITIES.stakeholder},
{value: 'project', label: StakeholderConfiguration.ENTITIES.project + ' ' + StakeholderConfiguration.ENTITIES.stakeholder},
{value: 'country', label: StakeholderConfiguration.ENTITIES.country + ' ' + StakeholderConfiguration.ENTITIES.stakeholder},
];
}

View File

@ -83,7 +83,11 @@ export class LayoutService {
* Add hasStickyHeaderOnMobile: true in order to activate uk-sticky in header of mobile/tablet devices.
* */
private hasStickyHeaderOnMobileSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
/**
* Add a class in root element of the html. (For different theme apply)
* Handle it manually in the component, it doesn't use data
* */
private rootClassSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);
private subscriptions: any[] = [];
ngOnDestroy() {
@ -329,4 +333,14 @@ export class LayoutService {
setHasStickyHeaderOnMobile(value: boolean) {
this.hasStickyHeaderOnMobileSubject.next(value);
}
get rootClass(): Observable<string> {
return this.rootClassSubject.asObservable();
}
setRootClass(value: string = null): void {
if(this.rootClassSubject.value != value) {
this.rootClassSubject.next(value);
}
}
}

View File

@ -28,6 +28,9 @@
[class.hide-on-close]="backItem.icon">{{backItem.title}}</span>
</a>
</div>
<div *ngIf="logoURL" id="sidebar_logo" class="uk-margin-top">
<img [src]="logoURL">
</div>
<div *ngIf="items.length > 0" class="menu_section uk-margin-large-top" [class.mobile]="mobile" style="min-height: 30vh">
<ul #nav class="uk-list uk-nav" [class.uk-list-large]="mobile"
[class.uk-nav-default]="!mobile" [class.uk-nav-primary]="mobile" uk-nav="duration: 400">

View File

@ -28,6 +28,7 @@ export class SideBarComponent implements OnInit, AfterViewInit, OnDestroy, OnCha
@Input() activeSubItem: string = '';
@Input() backItem: MenuItem = null;
@Input() queryParamsHandling;
@Input() logoURL: string;
@ViewChild("nav") nav: ElementRef;
@ViewChild("sidebar_offcanvas") sidebar_offcanvas: ElementRef;
public offset: number;

View File

@ -0,0 +1,48 @@
import {ChangeDetectorRef, Directive, OnInit} from "@angular/core";
import {BaseComponent} from "../../../sharedComponents/base/base.component";
import {MenuItem} from "../../../sharedComponents/menu";
import {LayoutService} from "./layout.service";
@Directive()
export class SidebarBaseComponent extends BaseComponent implements OnInit {
hasSidebar: boolean = false;
hasInternalSidebar: boolean = false;
hasAdminMenu: boolean = false;
/**
* Menu Items
* */
sideBarItems: MenuItem[] = [];
adminMenuItems: MenuItem[] = [];
backItem: MenuItem = null;
protected layoutService: LayoutService;
protected cdr: ChangeDetectorRef;
constructor() {
super();
}
ngOnInit() {
this.subscriptions.push(this.layoutService.hasSidebar.subscribe(hasSidebar => {
this.hasSidebar = hasSidebar;
this.cdr.detectChanges();
}));
this.subscriptions.push(this.layoutService.hasInternalSidebar.subscribe(hasInternalSidebar => {
this.hasInternalSidebar = hasInternalSidebar;
this.cdr.detectChanges();
}));
this.subscriptions.push(this.layoutService.hasAdminMenu.subscribe(hasAdminMenu => {
this.hasAdminMenu = hasAdminMenu;
this.cdr.detectChanges();
}));
this.layoutService.setOpen(true);
}
public get open() {
return this.layoutService.open;
}
public get hover() {
return this.layoutService.hover;
}
}

View File

@ -8,14 +8,7 @@ import {
SimpleChanges,
ViewChild
} from '@angular/core';
import {
UntypedFormArray,
UntypedFormBuilder,
UntypedFormControl,
UntypedFormGroup,
ValidatorFn,
Validators
} from '@angular/forms';
import {UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, ValidatorFn, Validators} from '@angular/forms';
import {AlertModal} from "../../../utils/modal/alert";
import {UserRegistryService} from "../../../services/user-registry.service";
import {EnvProperties} from "../../../utils/properties/env-properties";
@ -29,7 +22,6 @@ import {forkJoin, of, Subscription} from "rxjs";
import {NotificationHandler} from "../../../utils/notification-handler";
import {ClearCacheService} from "../../../services/clear-cache.service";
import {catchError, map, tap} from "rxjs/operators";
import notification = CKEDITOR.plugins.notification;
import {InputComponent} from "../../../sharedComponents/input/input.component";
class InvitationResponse {
@ -148,9 +140,8 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
updateLists() {
this.loadActive = true;
this.loadPending = true;
this.subs.push(this.userRegistryService.getActive(this.type, this.id, this.role).subscribe(users => {
this.subs.push(this.userRegistryService.getActive(this.type, this.id, this.role, true).subscribe(users => {
this.active = users;
// users.forEach(user => this.active.push(user));
this.filterActiveBySearch(this.filterForm.value.active);
this.loadActive = false;
this.exists = true;
@ -162,7 +153,7 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
}
this.loadActive = false;
}));
this.subs.push(this.userRegistryService.getPending(this.type, this.id, this.role).subscribe(users => {
this.subs.push(this.userRegistryService.getPending(this.type, this.id, this.role, true).subscribe(users => {
this.pending = users;
this.filterPendingBySearch(this.filterForm.value.pending);
this.loadPending = false;
@ -214,8 +205,8 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
this.createRoleModal.okButtonLeft = false;
this.createRoleModal.okButtonText = 'Create';
this.roleFb = this.fb.group({
name: this.fb.control(Role.mapType(this.type) + '.' + this.id, Validators.required),
description: this.fb.control(Role.mapType(this.type) + ' ' + this.id, Validators.required)
name: this.fb.control(Role.roleName(this.type, this.id), Validators.required),
description: this.fb.control(Role.roleName(this.type, this.id), Validators.required)
});
setTimeout(() => {
this.roleFb.get('name').disable();
@ -285,7 +276,7 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
if (!this.pending.includes(response.email)) {
this.pending.push(response.email);
}
if(this.notificationFn) {
if(this.notificationFn && properties.notificationsAPIURL) {
return this.notificationService.sendNotification(this.notificationFn(this.name, response.email, this.role, response.invitation)).pipe(map(notification => {
return notification;
}), tap(() => {

View File

@ -105,7 +105,7 @@ export class SubscribersComponent implements OnInit, OnDestroy, OnChanges {
updateList() {
this.loading = true;
this.subs.push( this.userRegistryService.getActive(this.type, this.id, 'member').subscribe(users => {
this.subs.push( this.userRegistryService.getActive(this.type, this.id, 'member', true).subscribe(users => {
this.subscribers = users;
this.filterBySearch(this.filterForm.value.keyword);
this.loading = false;

View File

@ -15,7 +15,7 @@ import {FullScreenModalComponent} from '../utils/modal/full-screen-modal/full-sc
@Component({
selector: 'deposit-first-page',
template: `
<div class="deposit">
<div>
<div class="uk-container uk-container-large uk-section uk-section-small uk-padding-remove-bottom">
<div class="uk-padding-small uk-padding-remove-horizontal">
<breadcrumbs [breadcrumbs]="breadcrumbs"></breadcrumbs>

View File

@ -11,7 +11,7 @@ import * as url from "url";
@Injectable()
export class ErrorInterceptorService implements HttpInterceptor {
private static UNAUTHORIZED_WHITELIST = [properties.userInfoUrl, properties.orcidAPIURL, properties.registryUrl? (properties.registryUrl + 'verification/'):null, properties.eoscDataTransferAPI].filter(value => !!value);
private static UNAUTHORIZED_WHITELIST = [properties.orcidAPIURL, properties.registryUrl? (properties.registryUrl + 'verification/'):null, properties.eoscDataTransferAPI].filter(value => !!value);
private url: string = null;
constructor(private router: Router) {

View File

@ -9,7 +9,8 @@
<landing-header *ngIf="resultLandingInfo" [properties]="properties" [title]="title"
[subTitle]="resultLandingInfo.subtitle" [underCuration]="resultLandingInfo.underCurationMessage"
[entityType]="entityType" [types]="resultLandingInfo.types"
[year]="resultLandingInfo.date" [embargoEndDate]="resultLandingInfo.embargoEndDate">
[year]="resultLandingInfo.date" [embargoEndDate]="resultLandingInfo.embargoEndDate"
[publiclyFunded]="resultLandingInfo.publiclyFunded" [projects]="resultLandingInfo.fundedByProjects">
</landing-header>
<landing-header *ngIf="organizationInfo" [properties]="properties" [title]="title"
[subTitle]="(organizationInfo.name && organizationInfo.title.name !== organizationInfo.name)?organizationInfo.name:null"

View File

@ -93,10 +93,12 @@ import {RouterHelper} from "../../utils/routerHelper.class";
{{instance.downloadNames.join("; ")}}
</a>
</span>
<div *ngIf="instance.types?.length > 0 || instance.years?.length > 0" class="uk-text-meta">
<div *ngIf="instance.types?.length > 0 || instance.years?.length > 0 || instance.peerReviewed" class="uk-text-meta">
<span *ngIf="instance.types?.length > 0" class="uk-text-capitalize">{{instance.types.join(" . ")}}</span>
<span *ngIf="instance.types?.length > 0 && instance.years?.length > 0"> . </span>
<span *ngIf="instance.years?.length > 0">{{instance.years.join(" . ")}}</span>
<span *ngIf="(instance.types?.length > 0 || instance.years?.length > 0) && instance.peerReviewed"> . </span>
<span *ngIf="instance.peerReviewed">Peer-reviewed</span>
</div>
<div *ngIf="instance.license" class="uk-text-meta uk-text-truncate" uk-tooltip [title]="instance.license">
License:

View File

@ -31,22 +31,20 @@ import {RouterHelper} from "../../utils/routerHelper.class";
<!-- types -->
<span *ngIf="entityType" class="uk-flex-inline uk-flex-middle uk-flex-wrap"
[class.other-separator]="types && removeUnknown(types, true).length > 0">
<span class="uk-margin-xsmall-right">
<icon *ngIf="entityType.toLowerCase() == 'publication'" name="description" type="outlined"
[flex]="true" [ratio]="0.8"></icon>
<icon *ngIf="entityType.toLowerCase() == 'research data'" name="database" type="outlined"
[flex]="true" [ratio]="0.8"></icon>
<icon *ngIf="entityType.toLowerCase() == 'research software'" name="integration_instructions"
type="outlined" [flex]="true" [ratio]="0.8"></icon>
<icon *ngIf="entityType.toLowerCase() == 'other research product'" name="apps" type="outlined"
[flex]="true" [ratio]="0.8"></icon>
<icon *ngIf="entityType.toLowerCase() == 'project'" name="assignment_turned_in" type="outlined"
[flex]="true" [ratio]="0.8"></icon>
<icon *ngIf="entityType.toLowerCase() == 'data source'" name="note_add" type="outlined"
[flex]="true" [ratio]="0.8"></icon>
<icon *ngIf="entityType.toLowerCase() == 'organization'" name="corporate_fare" type="outlined"
[flex]="true" [ratio]="0.8"></icon>
</span>
<icon *ngIf="entityType.toLowerCase() == 'publication'" name="description" type="outlined"
[flex]="true" [ratio]="0.8" class="uk-margin-xsmall-right"></icon>
<icon *ngIf="entityType.toLowerCase() == 'research data'" name="database" type="outlined"
[flex]="true" [ratio]="0.8" class="uk-margin-xsmall-right"></icon>
<icon *ngIf="entityType.toLowerCase() == 'research software'" name="integration_instructions"
type="outlined" [flex]="true" [ratio]="0.8" class="uk-margin-xsmall-right"></icon>
<icon *ngIf="entityType.toLowerCase() == 'other research product'" name="apps" type="outlined"
[flex]="true" [ratio]="0.8" class="uk-margin-xsmall-right"></icon>
<icon *ngIf="entityType.toLowerCase() == 'project'" name="assignment_turned_in" type="outlined"
[flex]="true" [ratio]="0.8" class="uk-margin-xsmall-right"></icon>
<icon *ngIf="entityType.toLowerCase() == 'data source'" name="note_add" type="outlined"
[flex]="true" [ratio]="0.8" class="uk-margin-xsmall-right"></icon>
<icon *ngIf="entityType.toLowerCase() == 'organization'" name="corporate_fare" type="outlined"
[flex]="true" [ratio]="0.8" class="uk-margin-xsmall-right"></icon>
<u class="uk-text-capitalize uk-text-bolder">{{entityType}}</u>
<span *ngIf="types && removeUnknown(types, true).length > 0">
<icon name="keyboard_double_arrow_right" [flex]="true" [ratio]="0.8"></icon>
@ -150,6 +148,9 @@ import {RouterHelper} from "../../utils/routerHelper.class";
<ng-container *ngIf="thematic">
<span>Thematic</span>
</ng-container>
<ng-container *ngIf="publiclyFunded">
<span>Publicly funded</span>
</ng-container>
<!-- Projects -->
<span *ngIf="projects && projects.length > 0"
[attr.uk-tooltip]="projects.length > projectsLimit ? 'cls: uk-invisible' : 'pos: top; cls: uk-active'" title="Funded by">
@ -246,6 +247,7 @@ export class EntityMetadataComponent {
@Input() type; // data provider landing
@Input() provenanceAction: string; // search result
@Input() relationName: string; // search result
@Input() publiclyFunded: boolean; // search result
@Input() projects: Project[];
@Input() organizations: Organization[];
@Input() subjects: string[];

View File

@ -0,0 +1,16 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {EntityMetadataComponent} from './entity-metadata.component';
import {IconsModule} from '../../utils/icons/icons.module';
import {AlertModalModule} from '../../utils/modal/alertModal.module';
import {ShowPublisherModule} from "./showPublisher.module";
@NgModule({
imports: [
CommonModule, IconsModule, AlertModalModule, ShowPublisherModule
],
declarations: [EntityMetadataComponent],
providers:[],
exports: [EntityMetadataComponent]
})
export class EntityMetadataModule { }

View File

@ -21,7 +21,7 @@ import {AlertModal} from "../../../utils/modal/alert";
[languages]="languages" [programmingLanguages]="programmingLanguages"
[compatibility]="compatibility" [aggregationStatus]="aggregationStatus"
[thematic]="thematic" [type]="type" [prevPath]="prevPath"
[countries]="countries" [projects]="projects"
[countries]="countries" [publiclyFunded]="publiclyFunded" [projects]="projects"
></entity-metadata>
</div>
<div *ngIf="authors">
@ -63,5 +63,6 @@ export class LandingHeaderComponent {
@Input() type; // data provider landing
@Input() prevPath: string = "";
@Input() countries;
@Input() publiclyFunded;
@Input() projects;
}

View File

@ -4,12 +4,12 @@ import {CommonModule} from "@angular/common";
import {LandingModule} from "../landing.module";
import {ShowAuthorsModule} from "../../../utils/authors/showAuthors.module";
import {IconsModule} from "src/app/openaireLibrary/utils/icons/icons.module";
import {ShowPublisherModule} from "../showPublisher.module";
import {RouterModule} from "@angular/router";
import {ResultLandingUtilsModule} from "../resultLandingUtils.module";
import {EntityMetadataModule} from "../entity-metadata.module";
@NgModule({
imports: [CommonModule, LandingModule, ShowAuthorsModule, IconsModule, ShowPublisherModule, RouterModule, ResultLandingUtilsModule],
imports: [CommonModule, LandingModule, ShowAuthorsModule, IconsModule, RouterModule, ResultLandingUtilsModule, EntityMetadataModule],
declarations: [LandingHeaderComponent],
exports: [LandingHeaderComponent]
})

View File

@ -265,7 +265,8 @@ export class ParsingFunctions {
"types": [],
"years": [],
"license": "",
"fulltext": ""
"fulltext": "",
"peerReviewed": null
};
if (instance.hasOwnProperty("hostedby")) {
@ -359,6 +360,10 @@ export class ParsingFunctions {
available.fulltext = instance['fulltext'];
}
if(instance.hasOwnProperty("refereed") && instance.refereed.classname == "peerReviewed") {
available.peerReviewed = true;
}
hostedBy_collectedFrom.push(available);
}

View File

@ -9,7 +9,6 @@ import {ShowSubjectsComponent} from './showSubjects.component';
import {FundedByComponent} from './fundedBy.component';
import {AvailableOnComponent} from './availableOn.component';
import {TabTableComponent} from './tabTable.component';
import {ShowPublisherComponent} from "./showPublisher.component";
import {ShowPublisherModule} from "./showPublisher.module";
import {RelatedToComponent} from "./relatedTo.component";
import {FosComponent} from "./fos.component";
@ -17,13 +16,11 @@ import {SdgComponent} from "./sdg.component";
import {IconsModule} from "../../utils/icons/icons.module";
import {AlertModalModule} from "../../utils/modal/alertModal.module";
import { SearchInputModule } from '../../sharedComponents/search-input/search-input.module';
import {EntityMetadataComponent} from "./entity-metadata.component";
import {IconsService} from "../../utils/icons/icons.service";
import {book, closed_access, cog, database, earth, open_access, unknown_access} from "../../utils/icons/icons";
import {FullScreenModalModule} from "../../utils/modal/full-screen-modal/full-screen-modal.module";
import {MobileDropdownModule} from "../../utils/mobile-dropdown/mobile-dropdown.module";
@NgModule({
imports: [
CommonModule, FormsModule, RouterModule, PagingModule, ShowPublisherModule, IconsModule, AlertModalModule,
@ -32,15 +29,14 @@ import {MobileDropdownModule} from "../../utils/mobile-dropdown/mobile-dropdown.
declarations: [
ShowIdentifiersComponent,ShowSubjectsComponent,
FundedByComponent,AvailableOnComponent,TabTableComponent,
RelatedToComponent, FosComponent, SdgComponent,
EntityMetadataComponent
RelatedToComponent, FosComponent, SdgComponent
],
providers:[
],
exports: [
ShowIdentifiersComponent, ShowSubjectsComponent,
FundedByComponent,AvailableOnComponent, TabTableComponent, ShowPublisherComponent,
RelatedToComponent, FosComponent, SdgComponent, EntityMetadataComponent
FundedByComponent,AvailableOnComponent, TabTableComponent,
RelatedToComponent, FosComponent, SdgComponent
]
})
export class ResultLandingUtilsModule {

View File

@ -27,7 +27,7 @@ import {HelperFunctions} from "../../../utils/HelperFunctions.class";
<ul class="uk-list uk-margin">
<li *ngFor="let result of results.slice((page-1)*pageSize, page*pageSize)">
<result-preview [modal]="modal" [properties]="properties" [hasLink]="false" [result]="getResultPreview(result)"
[showOrcid]="false" [isCard]="false" [prevPath]="prevPath" [showInline]="true"
[showOrcid]="false" [prevPath]="prevPath" [showInline]="true"
[isDeletedByInferenceModal]="true"></result-preview>
</li>
</ul>

View File

@ -229,6 +229,7 @@
[publisher]="resultLandingInfo.publisher" [journal]="resultLandingInfo.journal"
[languages]="resultLandingInfo.languages" [programmingLanguages]="resultLandingInfo.programmingLanguages"
[prevPath]="prevPath" [countries]="resultLandingInfo.countries"
[publiclyFunded]="resultLandingInfo.publiclyFunded"
[projects]="resultLandingInfo.fundedByProjects">
</landing-header>
<!-- Labels -->
@ -454,6 +455,29 @@
<!-- </div>-->
</div>
</div>
<div *ngIf="resultLandingInfo && resultLandingInfo.oaRoutes && !viewAll"
class="uk-margin-medium-top uk-padding uk-padding-remove-vertical">
<div class="landing-oaroutes-card uk-text-small uk-flex uk-padding-small">
<div>
<div class="uk-flex uk-flex-top" uk-grid>
<div *ngIf="resultLandingInfo.oaRoutes.green" class="uk-flex-nowrap uk-flex uk-flex-middle uk-margin-xsmall-bottom uk-margin-remove-top">
<div class="dot green"></div>
<div class="uk-text-capitalize">Green</div>
</div>
<div *ngIf="resultLandingInfo.oaRoutes.oaColor" class="uk-flex-nowrap uk-flex uk-flex-middle uk-margin-xsmall-bottom uk-margin-remove-top">
<div class="dot" [ngClass]="resultLandingInfo.oaRoutes.oaColor"></div>
<div class="uk-text-capitalize">{{resultLandingInfo.oaRoutes.oaColor}}</div>
</div>
</div>
<div *ngIf="resultLandingInfo.oaRoutes.isInDiamondJournal" class="uk-flex-nowrap uk-flex uk-flex-middle uk-margin-xsmall-bottom uk-text-truncate">
<div class="dot diamond"></div>
<div>Published in a Diamond OA journal</div>
</div>
</div>
</div>
</div>
<div class="uk-margin-medium-top uk-list uk-list-large uk-padding uk-padding-remove-vertical" [class.uk-list-divider]="!viewAll">
<!-- EOSC Services-->
<div *ngIf="resultLandingInfo.eoscSubjects?.length > 0 && properties.adminToolsPortalType == 'eosc'
@ -523,6 +547,7 @@
[date]="resultLandingInfo.dateofacceptance" [embargoEndDate]="resultLandingInfo.embargoEndDate"
[publisher]="resultLandingInfo.publisher" [journal]="resultLandingInfo.journal"
[languages]="resultLandingInfo.languages" [programmingLanguages]="resultLandingInfo.programmingLanguages"
[publiclyFunded]="resultLandingInfo.publiclyFunded" [projects]="resultLandingInfo.fundedByProjects"
[isMobile]="true" [prevPath]="prevPath">
</landing-header>
<div class="uk-text-small">
@ -540,6 +565,31 @@
<fos [subjects]="resultLandingInfo.fos" (suggestClicked)="suggestMobileClicked($event)"
(viewAllClicked)="viewAllMobile=$event; openFsModal(fosFsModal, 'Fields of Science (FoS)')"></fos>
</div>
<!-- OA Routes -->
<div *ngIf="resultLandingInfo.oaRoutes && !viewAllMobile" class="uk-margin-top uk-grid uk-grid-small uk-grid-divider" uk-grid>
<div class="uk-width-auto uk-text-meta">
Access Routes
</div>
<div class="uk-width-expand uk-flex">
<div>
<div class="uk-flex uk-flex-top" uk-grid>
<div *ngIf="resultLandingInfo.oaRoutes.green" class="uk-flex-nowrap uk-flex uk-flex-middle uk-margin-xsmall-bottom uk-margin-remove-top">
<div class="dot green"></div>
<div class="uk-text-capitalize">Green</div>
</div>
<div *ngIf="resultLandingInfo.oaRoutes.oaColor" class="uk-flex-nowrap uk-flex uk-flex-middle uk-margin-xsmall-bottom uk-margin-remove-top">
<div class="dot" [ngClass]="resultLandingInfo.oaRoutes.oaColor"></div>
<div class="uk-text-capitalize">{{resultLandingInfo.oaRoutes.oaColor}}</div>
</div>
</div>
<div *ngIf="resultLandingInfo.oaRoutes.isInDiamondJournal" class="uk-flex-nowrap uk-flex uk-flex-middle uk-margin-xsmall-bottom uk-text-truncate">
<div class="dot diamond"></div>
<div>Published in a Diamond OA journal</div>
</div>
</div>
</div>
</div>
</div>
<div class="uk-section uk-margin-top uk-text-large uk-text-empashis uk-text-bold">
<hr>

View File

@ -142,6 +142,19 @@ export class ResultLandingService {
this.resultLandingInfo.publisher = data[0].publisher;
this.resultLandingInfo.description = this.parsingFunctions.parseDescription(data[0] && data[0].description ? data[0].description : []);
this.resultLandingInfo.embargoEndDate = data[0].embargoenddate ? Dates.getDate(data[0].embargoenddate) : null;
if(data[0].hasOwnProperty("publiclyfunded") && data[0].publiclyfunded) {
this.resultLandingInfo.publiclyFunded = data[0].publiclyfunded;
}
if((data[0].hasOwnProperty("isgreen") && data[0].isgreen)
|| (data[0].hasOwnProperty("openaccesscolor") && data[0].openaccesscolor)
|| (data[0].hasOwnProperty("isindiamondjournal") && data[0].isindiamondjournal)) {
this.resultLandingInfo.oaRoutes = {
"green": data[0].isgreen,
"oaColor": data[0].openaccesscolor,
"isInDiamondJournal":data[0].isindiamondjournal
};
}
}
if (data[0]['bestaccessright'] && data[0]['bestaccessright'].hasOwnProperty("classname")) {
@ -325,11 +338,13 @@ export class ResultLandingService {
if (this.resultLandingInfo.fos) {
this.resultLandingInfo.fos.sort((a, b) => a.id.localeCompare(b.id));
}
this.resultLandingInfo.sdg = subjectResults[4];
if (this.resultLandingInfo.sdg) {
this.resultLandingInfo.sdg.sort((a, b) => {
return HelperFunctions.sortSDGs(a, b);
})
if(properties.dashboard != "irish") {
this.resultLandingInfo.sdg = subjectResults[4];
if (this.resultLandingInfo.sdg) {
this.resultLandingInfo.sdg.sort((a, b) => {
return HelperFunctions.sortSDGs(a, b);
})
}
}
// if(!this.resultLandingInfo.eoscSubjects) {

View File

@ -27,7 +27,6 @@ export class UserComponent {
public errorCode: string = "";
public redirectUrl: string = "";
public routerHelper: RouterHelper = new RouterHelper();
public loginUrl;
public properties: EnvProperties = properties;
@Input() mainComponent = true;
@ -40,7 +39,6 @@ export class UserComponent {
}
ngOnInit() {
this.loginUrl = this.properties.loginUrl;
if (typeof document !== 'undefined') {
this.server = false;
this.userManagementsService.updateUserInfo( () => {

View File

@ -122,12 +122,8 @@ export class UserMiniComponent implements OnInit, OnChanges {
public isAuthorized: boolean = false;
@Input() public mobileView: boolean = false;
public firstLetters: string = "";
public server: boolean = true;
public routerHelper: RouterHelper = new RouterHelper();
@Input() userMenuItems;
@Input() logInUrl;
@Input() logOutUrl;
@Input() cookieDomain;
@Input() notificationConfiguration: NotificationConfiguration;
@ViewChild('notificationsSidebar') notificationsSidebar: NotificationsSidebarComponent;
public showNotifications = false;
@ -137,9 +133,6 @@ export class UserMiniComponent implements OnInit, OnChanges {
}
ngOnInit() {
if (typeof document !== 'undefined') {
this.server = false;
}
this.initUser();
}

View File

@ -1,4 +1,5 @@
import {stakeholderTypes} from "../../monitor/entities/stakeholder";
import {StakeholderConfiguration} from "../../monitor-admin/utils/indicator-utils";
export class User {
email: string;
@ -9,6 +10,7 @@ export class User {
expirationDate: number;
role: string[];
accessToken?: string;
orcid?: string;
refreshToken?: string;
constructor(info: any) {
@ -22,6 +24,9 @@ export class User {
if(info.refreshToken) {
this.refreshToken = info.refreshToken;
}
if(info.orcid) {
this.orcid = info.orcid;
}
this.fullname = (info.name) ? info.name : "";
if (this.fullname == "") {
if (this.firstname != "") {
@ -37,7 +42,7 @@ export class User {
this.role = [];
if (info.roles) {
info.roles.forEach(role => {
this.role.push(role);
this.role.push(decodeURIComponent(role).replace('$$', '::'));
});
} else {
if (info.edu_person_entitlements) {
@ -63,20 +68,31 @@ export class Session {
var cookie = COOKIE.getCookie(COOKIE.cookieName_id);
return (cookie != null && cookie != "");
}
public static clearReloadUrl() {
COOKIE.setCookie("reloadURLs", JSON.stringify([]), -1);
}
public static setReloadUrl(host: string, path: string, params: string, fragment: string) {
var URL = {};
let URLs:any[] = this.getReloadUrl();
let URL = {};
URL["host"] = host;
URL["path"] = path;
URL["params"] = params;
URL["fragment"] = fragment;
COOKIE.setCookie("reloadURL", JSON.stringify(URL), -1);
URLs.push(URL);
COOKIE.setCookie("reloadURLs", JSON.stringify(URLs), -1);
}
public static getReloadUrl() {
var URL = COOKIE.getCookie("reloadURL");
URL = JSON.parse(URL);
return URL;
let URLs = COOKIE.getCookie("reloadURLs");
let array = JSON.parse(URLs);
return array?array:[];
}
public static popReloadUrl() {
let array = this.getReloadUrl();
let Url = array.length>0?array[0]:null;
COOKIE.setCookie("reloadURLs", JSON.stringify(array.slice(1)), -1);
return Url;
}
@ -100,7 +116,7 @@ export class Session {
}
public static isMonitorCurator(user: User): boolean {
return stakeholderTypes.filter(stakeholderType => this.isTypeCurator(stakeholderType.value, user)).length > 0;
return StakeholderConfiguration.TYPES.filter(stakeholderType => this.isTypeCurator(stakeholderType.value, user)).length > 0;
}
public static isCommunityCurator(user: User): boolean {
@ -112,7 +128,7 @@ export class Session {
}
public static isCurator(type: string, user: User): boolean {
return (type === 'community' || stakeholderTypes.find(stakeholderType => stakeholderType.value == type)) && this.isTypeCurator(type, user);
return (type === 'community' || StakeholderConfiguration.TYPES.find(stakeholderType => stakeholderType.value == type)) && this.isTypeCurator(type, user);
}
public static isPortalAdministrator(user: User): boolean {
@ -206,12 +222,16 @@ export class COOKIE {
}
export class Role {
public static GROUP = '';
public static PORTAL_ADMIN = 'PORTAL_ADMINISTRATOR';
public static REGISTERED_USER = 'REGISTERED_USER';
public static ANONYMOUS_USER = 'ROLE_ANONYMOUS';
public static USER_MANAGER = 'USER_MANAGER';
public static CURATOR_CLAIM = 'CURATOR_CLAIM';
public static roleName(type: string, id: string) {
return this.GROUP + this.mapType(type) + '.' + id;
}
public static mapType(type: string, communityMap: boolean = true): string {
if (type == "ri" && communityMap) {
@ -219,7 +239,7 @@ export class Role {
} else if (type == "organization") {
type = "institution";
}
return type;
return Role.GROUP + type;
}
/**

View File

@ -1,11 +1,8 @@
import {Component, Input, OnDestroy, ViewChild} from "@angular/core";
import {Component, Input, ViewChild} from "@angular/core";
import {Stakeholder} from "../../../monitor/entities/stakeholder";
import {UntypedFormBuilder, UntypedFormGroup, Validators} from "@angular/forms";
import {StakeholderUtils} from "../../utils/indicator-utils";
import {Option} from "../../../sharedComponents/input/input.component";
import {Subscription} from "rxjs";
import {EnvProperties} from "../../../utils/properties/env-properties";
import {properties} from "src/environments/environment";
import {StakeholderService} from "../../../monitor/services/stakeholder.service";
import {UtilitiesService} from "../../../services/utilities.service";
import {Role, Session, User} from "../../../login/utils/helper.class";
@ -16,121 +13,132 @@ import {NotificationUtils} from "../../../notifications/notification-utils";
import {Notification} from "../../../notifications/notifications";
import {NotificationHandler} from "../../../utils/notification-handler";
import {StatsProfilesService} from "../../utils/services/stats-profiles.service";
import {StakeholderBaseComponent} from "../../utils/stakeholder-base.component";
@Component({
selector: 'edit-stakeholder',
template: `
<form *ngIf="stakeholderFb" [formGroup]="stakeholderFb">
<div class="uk-grid uk-grid-large" uk-grid>
<div class="uk-width-1-2@m">
<div input id="name" [formInput]="stakeholderFb.get('name')"
placeholder="Name"></div>
</div>
<div class="uk-width-1-2@m">
<div input [formInput]="stakeholderFb.get('alias')"
placeholder="URL Alias"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_id')"
placeholder="Index ID"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_name')"
placeholder="Index Name"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_shortName')"
placeholder="Index Short Name"></div>
</div>
<ng-container *ngIf="isCurator">
<div class="uk-width-1-3@m">
<div *ngIf="statsProfiles" input [formInput]="stakeholderFb.get('statsProfile')" [type]="'select'"
[options]="statsProfiles"
placeholder="Stats Profile"></div>
<div class="uk-margin-medium-bottom">
<form *ngIf="stakeholderFb" [formGroup]="stakeholderFb">
<div class="uk-grid uk-grid-large" uk-grid>
<div class="uk-width-1-2@m">
<div input id="name" [formInput]="stakeholderFb.get('name')"
placeholder="Name"></div>
</div>
<div class="uk-width-1-2@m">
<div input [formInput]="stakeholderFb.get('alias')"
placeholder="URL Alias"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('projectUpdateDate')" [type]="'date'"
placeholder="Last Project Update"></div>
<div input [formInput]="stakeholderFb.get('index_id')"
placeholder="Index ID"></div>
</div>
</ng-container>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('locale')" [type]="'select'"
[options]="stakeholderUtils.locales"
placeholder="Locale"></div>
</div>
<div class="uk-width-1-1">
<div input [type]="'textarea'" placeholder="Description"
[rows]="4" [formInput]="stakeholderFb.get('description')"></div>
</div>
<div class="uk-width-1-1">
<input #file id="photo" type="file" class="uk-hidden" (change)="fileChangeEvent($event)"/>
<div *ngIf="!stakeholderFb.get('isUpload').value" class="uk-grid uk-grid-column-large" uk-grid>
<div class="uk-margin-top uk-width-auto@l uk-width-1-1">
<div class="uk-grid uk-grid-column-large uk-flex-middle" uk-grid>
<div class="uk-width-auto@l uk-width-1-1 uk-flex uk-flex-center">
<button class="uk-button uk-button-primary uk-flex uk-flex-middle uk-flex-wrap"
(click)="file.click()">
<icon name="cloud_upload" [flex]="true"></icon>
<span class="uk-margin-small-left">Upload a file</span>
</button>
</div>
<div class="uk-text-center uk-text-bold uk-width-expand">
OR
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_name')"
placeholder="Index Name"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_shortName')"
placeholder="Index Short Name"></div>
</div>
<ng-container *ngIf="isCurator">
<div class="uk-width-1-3@m">
<div *ngIf="statsProfiles" input [formInput]="stakeholderFb.get('statsProfile')"
[type]="'select'"
[options]="statsProfiles"
placeholder="Stats Profile"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('projectUpdateDate')" [type]="'date'"
placeholder="Last Project Update"></div>
</div>
</ng-container>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('locale')" [type]="'select'"
[options]="stakeholderUtils.locales"
placeholder="Locale"></div>
</div>
<div class="uk-width-1-1">
<div input [type]="'textarea'" placeholder="Description"
[rows]="4" [formInput]="stakeholderFb.get('description')"></div>
</div>
<div class="uk-width-1-1">
<input #file id="photo" type="file" class="uk-hidden" (change)="fileChangeEvent($event)"/>
<div *ngIf="!stakeholderFb.get('isUpload').value" class="uk-grid uk-grid-column-large" uk-grid>
<div class="uk-margin-xsmall-top uk-width-auto@l uk-width-1-1">
<div class="uk-grid uk-grid-column-large uk-flex-middle" uk-grid>
<div class="uk-width-auto@l uk-width-1-1 uk-flex uk-flex-center">
<button class="uk-button uk-button-primary uk-flex uk-flex-middle uk-flex-wrap"
(click)="file.click()">
<icon name="cloud_upload" [flex]="true"></icon>
<span class="uk-margin-small-left">Upload a file</span>
</button>
</div>
<div class="uk-text-center uk-text-bold uk-width-expand">
OR
</div>
</div>
</div>
<div input class="uk-width-expand" type="logoURL" [placeholder]="'Link to the logo'"
[formInput]="stakeholderFb.get('logoUrl')"></div>
</div>
<div input class="uk-width-expand" type="logoURL" [placeholder]="'Link to the logo'"
[formInput]="stakeholderFb.get('logoUrl')"></div>
<div *ngIf="stakeholderFb.get('isUpload').value" class="uk-width-1-1 uk-flex uk-flex-middle">
<div class="uk-card uk-card-default uk-text-center uk-border-circle">
<img class="uk-position-center uk-blend-multiply" [src]="photo">
</div>
<div class="uk-margin-left">
<button (click)="remove()" class="uk-button-danger uk-icon-button uk-icon-button-small">
<icon [flex]="true" ratio="0.8" name="delete"></icon>
</button>
</div>
<div class="uk-margin-small-left">
<button class="uk-button-secondary uk-icon-button uk-icon-button-small"
(click)="file.click()">
<icon [flex]="true" ratio="0.8" name="edit"></icon>
</button>
</div>
</div>
<!-- Full width error message -->
<div *ngIf="uploadError"
class="uk-text-danger uk-margin-small-top uk-width-1-1">{{uploadError}}</div>
</div>
<div *ngIf="stakeholderFb.get('isUpload').value" class="uk-width-1-1 uk-flex uk-flex-middle">
<div class="uk-card uk-card-default uk-text-center uk-border-circle">
<img class="uk-position-center uk-blend-multiply" [src]="photo">
</div>
<div class="uk-margin-left">
<button (click)="remove()" class="uk-button-danger uk-icon-button uk-icon-button-small">
<icon [flex]="true" ratio="0.8" name="delete"></icon>
</button>
</div>
<div class="uk-margin-small-left">
<button class="uk-button-secondary uk-icon-button uk-icon-button-small"
(click)="file.click()">
<icon [flex]="true" ratio="0.8" name="edit"></icon>
</button>
<div class="uk-width-1-1">
<div class="uk-grid uk-child-width-expand@m uk-child-width-1-1" uk-grid>
<div *ngIf="showVisibility">
<div input [formInput]="stakeholderFb.get('visibility')"
[placeholder]="'Select a status'"
[options]="stakeholderUtils.visibilities" type="select"></div>
</div>
<div [class.uk-width-1-2@m]="!showVisibility && !showFunderType && !canChooseTemplate">
<div input [formInput]="stakeholderFb.get('type')"
[placeholder]="'Select a type of ' + entities.stakeholder"
[options]="typesByRole" type="select"></div>
</div>
<div *ngIf="showFunderType">
<div input [formInput]="stakeholderFb.get('funderType')"
[placeholder]="'Select a type of ' + entities.funder"
[options]="stakeholderUtils.funderTypes" type="select"></div>
</div>
<div *ngIf="canChooseTemplate">
<div [placeholder]="'Select a template'"
input [formInput]="stakeholderFb.get('defaultId')"
[options]="defaultStakeholdersOptions" type="select"></div>
</div>
</div>
</div>
<!-- Full width error message -->
<div *ngIf="uploadError" class="uk-text-danger uk-margin-small-top uk-width-1-1">{{uploadError}}</div>
</div>
<div [class]="canChooseTemplate ? 'uk-width-1-3@m' : 'uk-width-1-2@m'">
<div input [formInput]="stakeholderFb.get('visibility')"
[placeholder]="'Select a status'"
[options]="stakeholderUtils.statuses" type="select"></div>
</div>
<div [class]="canChooseTemplate ? 'uk-width-1-3@m' : 'uk-width-1-2@m'">
<div input [formInput]="stakeholderFb.get('type')"
[placeholder]="'Select a type'"
[options]="types" type="select"></div>
</div>
<ng-container *ngIf="canChooseTemplate">
<div class="uk-width-1-3@m">
<div [placeholder]="'Select a template'"
input [formInput]="stakeholderFb.get('defaultId')"
[options]="defaultStakeholdersOptions" type="select"></div>
</div>
</ng-container>
</div>
</form>
<div #notify [class.uk-hidden]="!stakeholderFb" notify-form
class="uk-width-1-1 uk-margin-large-top uk-margin-medium-bottom"></div>
</form>
<div #notify [class.uk-hidden]="!stakeholderFb" notify-form
class="uk-width-1-1 uk-margin-large-top"></div>
</div>
`,
styleUrls: ['edit-stakeholder.component.less']
})
export class EditStakeholderComponent implements OnDestroy {
export class EditStakeholderComponent extends StakeholderBaseComponent {
@Input()
public disableAlias: boolean = false;
public stakeholderFb: UntypedFormGroup;
public secure: boolean = false;
public stakeholderUtils: StakeholderUtils = new StakeholderUtils();
public defaultStakeholdersOptions: Option[];
public defaultStakeholders: Stakeholder[];
public alias: string[];
@ -138,10 +146,8 @@ export class EditStakeholderComponent implements OnDestroy {
public isDefault: boolean;
public isNew: boolean;
public loading: boolean = false;
public types: Option[];
public typesByRole: Option[];
public statsProfiles: string[];
public properties: EnvProperties = properties;
private subscriptions: any[] = [];
/**
* Photo upload
* */
@ -158,16 +164,21 @@ export class EditStakeholderComponent implements OnDestroy {
private stakeholderService: StakeholderService,
private statsProfileService: StatsProfilesService,
private utilsService: UtilitiesService, private userManagementService: UserManagementService,) {
super();
}
ngOnDestroy() {
this.reset();
super.ngOnDestroy();
}
public init(stakeholder: Stakeholder, alias: string[], defaultStakeholders: Stakeholder[], isDefault: boolean, isNew: boolean) {
this.reset();
this.deleteCurrentPhoto = false;
this.stakeholder = stakeholder;
if (this.stakeholderUtils.defaultValue(this.stakeholderUtils.visibilities)) {
this.stakeholder.visibility = this.stakeholderUtils.defaultValue(this.stakeholderUtils.visibilities);
}
this.alias = alias;
this.defaultStakeholders = defaultStakeholders;
this.isDefault = isDefault;
@ -183,7 +194,7 @@ export class EditStakeholderComponent implements OnDestroy {
} else {
this.statsProfiles = [];
}
this.types = this.stakeholderUtils.getTypesByUserRoles(this.user, this.stakeholder.alias);
this.typesByRole = this.stakeholderUtils.getTypesByUserRoles(this.user, this.stakeholder.alias);
this.stakeholderFb = this.fb.group({
_id: this.fb.control(this.stakeholder._id),
defaultId: this.fb.control(this.stakeholder.defaultId),
@ -191,21 +202,22 @@ export class EditStakeholderComponent implements OnDestroy {
description: this.fb.control(this.stakeholder.description),
index_name: this.fb.control(this.stakeholder.index_name, Validators.required),
index_id: this.fb.control(this.stakeholder.index_id, Validators.required),
index_shortName: this.fb.control(this.stakeholder.index_shortName, Validators.required),
index_shortName: this.fb.control(this.stakeholder.index_shortName),
statsProfile: this.fb.control(this.stakeholder.statsProfile, Validators.required),
locale: this.fb.control(this.stakeholder.locale, Validators.required),
projectUpdateDate: this.fb.control(this.stakeholder.projectUpdateDate),
creationDate: this.fb.control(this.stakeholder.creationDate),
alias: this.fb.control(this.stakeholder.alias,
[
Validators.required,
this.stakeholderUtils.aliasValidatorString(
this.alias.filter(alias => alias !== this.stakeholder.alias)
)]
[
Validators.required,
this.stakeholderUtils.aliasValidatorString(
this.alias.filter(alias => alias !== this.stakeholder.alias)
)]
),
isDefault: this.fb.control((this.isDefault)),
visibility: this.fb.control(this.stakeholder.visibility, Validators.required),
type: this.fb.control(this.stakeholder.type, Validators.required),
funderType: this.fb.control(this.stakeholder.funderType),
topics: this.fb.control(this.stakeholder.topics),
isUpload: this.fb.control(this.stakeholder.isUpload),
logoUrl: this.fb.control(this.stakeholder.logoUrl),
@ -234,7 +246,7 @@ export class EditStakeholderComponent implements OnDestroy {
this.subscriptions.push(this.stakeholderFb.get('type').valueChanges.subscribe(value => {
this.onTypeChange(value, defaultStakeholders);
}));
this.stakeholderFb.setControl('defaultId', this.fb.control(stakeholder.defaultId, (this.isDefault && !this.isNew)?[]:Validators.required));
this.stakeholderFb.setControl('defaultId', this.fb.control(stakeholder.defaultId, (this.isDefault && !this.isNew) ? [] : Validators.required));
if (!this.isNew) {
this.notification = NotificationUtils.editStakeholder(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
this.notify.reset(this.notification.message);
@ -280,8 +292,8 @@ export class EditStakeholderComponent implements OnDestroy {
public get disabled(): boolean {
return (this.stakeholderFb && this.stakeholderFb.invalid) ||
(this.stakeholderFb && this.stakeholderFb.pristine && !this.isNew && !this.file) ||
(this.uploadError && this.uploadError.length > 0);
(this.stakeholderFb && this.stakeholderFb.pristine && !this.isNew && !this.file) ||
(this.uploadError && this.uploadError.length > 0);
}
public get dirty(): boolean {
@ -303,7 +315,7 @@ export class EditStakeholderComponent implements OnDestroy {
}
onTypeChange(value, defaultStakeholders: Stakeholder[]) {
this.stakeholderFb.setControl('defaultId', this.fb.control(this.stakeholder.defaultId, (this.isDefault && !this.isNew)?[]:Validators.required));
this.stakeholderFb.setControl('defaultId', this.fb.control(this.stakeholder.defaultId, (this.isDefault && !this.isNew) ? [] : Validators.required));
this.defaultStakeholdersOptions = [{
label: 'New blank profile',
value: '-1'
@ -340,9 +352,9 @@ export class EditStakeholderComponent implements OnDestroy {
if (this.isNew) {
let defaultStakeholder = this.defaultStakeholders.find(value => value._id === this.stakeholderFb.getRawValue().defaultId);
this.stakeholderFb.setValue(this.stakeholderUtils.createFunderFromDefaultProfile(this.stakeholderFb.getRawValue(),
(defaultStakeholder ? defaultStakeholder.topics : []), this.stakeholderFb.getRawValue().isDefault));
(defaultStakeholder ? defaultStakeholder.topics : []), this.stakeholderFb.getRawValue().isDefault));
this.removePhoto();
if(this.stakeholderFb.getRawValue().isDefault) {
if (this.stakeholderFb.getRawValue().isDefault) {
this.stakeholderFb.get('defaultId').setValue(null);
}
this.subscriptions.push(this.stakeholderService.buildStakeholder(this.properties.monitorServiceAPIURL,
@ -437,7 +449,11 @@ export class EditStakeholderComponent implements OnDestroy {
public deletePhoto() {
if (this.stakeholder.logoUrl && this.stakeholder.isUpload) {
this.subscriptions.push(this.utilsService.deletePhoto(this.properties.utilsService + '/delete/' +
encodeURIComponent(this.stakeholder.type) + "/" + encodeURIComponent(this.stakeholder.alias) + "/" + this.stakeholder.logoUrl).subscribe());
encodeURIComponent(this.stakeholder.type) + "/" + encodeURIComponent(this.stakeholder.alias) + "/" + this.stakeholder.logoUrl).subscribe());
}
}
get showFunderType() {
return super.showFunderType && this.stakeholderFb?.get('type').value === 'funder' && !this.isDefault;
}
}

View File

@ -1,29 +1,28 @@
import {ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild} from "@angular/core";
import {ChangeDetectorRef, Component, OnInit, ViewChild} from "@angular/core";
import {StakeholderService} from "../../monitor/services/stakeholder.service";
import {EnvProperties} from "../../utils/properties/env-properties";
import {Stakeholder} from "../../monitor/entities/stakeholder";
import { Subscription, zip} from "rxjs";
import {zip} from "rxjs";
import {EditStakeholderComponent} from "./edit-stakeholder/edit-stakeholder.component";
import {properties} from "src/environments/environment";
import {Title} from "@angular/platform-browser";
import {BaseComponent} from "../../sharedComponents/base/base.component";
import {ActivatedRoute} from "@angular/router";
@Component({
selector: 'general',
templateUrl: "./general.component.html"
})
export class GeneralComponent implements OnInit, OnDestroy {
export class GeneralComponent extends BaseComponent implements OnInit {
public stakeholder: Stakeholder;
public alias: string[];
public properties: EnvProperties = properties;
public defaultStakeholders: Stakeholder[];
public loading: boolean = false;
private subscriptions: any[] = [];
@ViewChild('editStakeholderComponent') editStakeholderComponent: EditStakeholderComponent;
constructor(private stakeholderService: StakeholderService,
private cdr: ChangeDetectorRef,
private title: Title) {
protected _route: ActivatedRoute,
protected _title: Title) {
super();
}
ngOnInit() {
@ -32,7 +31,8 @@ export class GeneralComponent implements OnInit, OnDestroy {
this.stakeholder = stakeholder;
this.cdr.detectChanges();
if(this.stakeholder) {
this.title.setTitle(this.stakeholder.name + " | General");
this.title = this.stakeholder.name + " | General";
this.setMetadata();
let data = zip(
this.stakeholderService.getDefaultStakeholders(this.properties.monitorServiceAPIURL),
this.stakeholderService.getAlias(this.properties.monitorServiceAPIURL)
@ -51,10 +51,9 @@ export class GeneralComponent implements OnInit, OnDestroy {
this.editStakeholderComponent.init(this.stakeholder, this.alias, this.defaultStakeholders, this.stakeholder.defaultId == null, false)
}
public save() {
this.loading = true;
this.editStakeholderComponent.save((stakeholder) => {
this.editStakeholderComponent.save(stakeholder => {
this.stakeholder = stakeholder;
this.stakeholderService.setStakeholder(this.stakeholder);
this.reset();
@ -64,12 +63,4 @@ export class GeneralComponent implements OnInit, OnDestroy {
this.loading = false;
});
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => {
if(subscription instanceof Subscription) {
subscription.unsubscribe();
}
});
}
}

View File

@ -1,138 +1,173 @@
<div page-content>
<div header>
<sidebar-mobile-toggle class="uk-margin-top uk-hidden@m uk-display-block"></sidebar-mobile-toggle>
<div *ngIf="isCurator()" class="uk-margin-remove-bottom uk-margin-medium-top">
<slider-tabs [type]="'dynamic'" (activeEmitter)="tab = $event">
<slider-tab tabTitle="All" [tabId]="'all'" [active]="tab === 'all'"></slider-tab>
<slider-tab tabTitle="Profile templates" [tabId]="'templates'" [active]="tab === 'templates'"></slider-tab>
<slider-tab tabTitle="Profiles" [tabId]="'profiles'" [active]="tab === 'profiles'"></slider-tab>
</slider-tabs>
<div header>
<sidebar-mobile-toggle class="uk-margin-top uk-hidden@m uk-display-block"></sidebar-mobile-toggle>
<div *ngIf="isCurator()" class="uk-margin-remove-bottom uk-margin-medium-top">
<slider-tabs [type]="'dynamic'" (activeEmitter)="tab = $event">
<slider-tab tabTitle="All" [tabId]="'all'" [active]="tab === 'all'"></slider-tab>
<slider-tab tabTitle="Profile templates" [tabId]="'templates'"
[active]="tab === 'templates'"></slider-tab>
<slider-tab tabTitle="Profiles" [tabId]="'profiles'" [active]="tab === 'profiles'"></slider-tab>
</slider-tabs>
</div>
</div>
</div>
<div actions>
<div class="uk-section-xsmall">
<div class="uk-flex uk-flex-right@m uk-flex-center uk-flex-wrap uk-flex-middle">
<div search-input [searchControl]="filters.get('keyword')" [expandable]="true" placeholder="Search Profiles" searchInputClass="outer"
class="uk-width-1-3@xl uk-width-2-5@l uk-width-1-2@m uk-width-1-1 uk-flex uk-flex-right"></div>
</div>
<div actions>
<div class="uk-section-xsmall">
<div class="uk-flex uk-flex-center uk-flex-wrap uk-flex-middle"
[ngClass]="properties.dashboard == 'irish' ? 'uk-flex-between@m' : 'uk-flex-right@m'">
<div *ngIf="properties.dashboard == 'irish'" class="uk-width-medium uk-margin-small-bottom">
<div input type="select" placeholder="Type"
[options]="typeOptions" [formInput]="filters.get('type')">
</div>
</div>
<div search-input [searchControl]="filters.get('keyword')" [expandable]="true"
placeholder="Search Profiles" searchInputClass="outer"
class="uk-width-1-3@xl uk-width-2-5@l uk-width-1-2@m uk-width-1-1 uk-flex uk-flex-right"></div>
</div>
</div>
</div>
</div>
<div inner>
<div *ngIf="loading" class="uk-margin-medium-top uk-padding-large">
<loading></loading>
</div>
<div *ngIf="!loading" uk-height-match="target: .titleContainer; row: false">
<div uk-height-match="target: .logoContainer; row: false">
<div *ngIf="tab != 'profiles' && isCurator()" class="uk-section">
<h4>Profile Templates</h4>
<div class="uk-grid uk-child-width-1-3@l uk-child-width-1-2@m uk-child-width-1-1 uk-grid-match" uk-grid>
<ng-template ngFor [ngForOf]="displayDefaultStakeholders" let-stakeholder>
<ng-container *ngTemplateOutlet="stakeholderBox; context: {stakeholder:stakeholder}"></ng-container>
</ng-template>
<div *ngIf="!loading && isCurator()">
<ng-container *ngTemplateOutlet="newBox; context: {text:'Create a new default profile.', isDefault:true}"></ng-container>
</div>
</div>
</div>
<div *ngIf="!isManager()" class="message">
<h4 class="uk-text-center">
No profiles to manage yet
</h4>
</div>
<div *ngIf="tab != 'templates' && isManager()" class="uk-section">
<h4>Profiles</h4>
<div class="uk-grid uk-grid-match uk-child-width-1-3@l uk-child-width-1-2@m uk-child-width-1-1" uk-grid>
<ng-template ngFor [ngForOf]="displayStakeholders" let-stakeholder>
<ng-container *ngTemplateOutlet="stakeholderBox; context: {stakeholder:stakeholder}"></ng-container>
</ng-template>
<div *ngIf="!loading && isCurator()">
<ng-container *ngTemplateOutlet="newBox; context: {text:'Create a new profile by selecting the type ('+typesAsString+') and ' +
<div inner>
<div *ngIf="loading" class="uk-margin-medium-top uk-padding-large">
<loading></loading>
</div>
<div *ngIf="!loading" uk-height-match="target: .titleContainer; row: false">
<div uk-height-match="target: .logoContainer; row: false">
<div *ngIf="tab != 'profiles' && isCurator()" class="uk-section">
<div class="uk-flex uk-flex-middle uk-flex-between uk-margin-small-bottom">
<h4 class="uk-margin-remove">Profile Templates</h4>
<paging-no-load *ngIf="displayDefaultStakeholders?.length > pageSize"
(pageChange)="updateCurrentTemplatesPage($event)"
[currentPage]="currentTemplatesPage" [size]="pageSize"
[totalResults]="displayDefaultStakeholders.length">
</paging-no-load>
</div>
<div class="uk-grid uk-child-width-1-3@l uk-child-width-1-2@m uk-child-width-1-1 uk-grid-match"
uk-grid>
<ng-template ngFor
[ngForOf]="displayDefaultStakeholders.slice((currentTemplatesPage-1)*pageSize, currentTemplatesPage*pageSize)"
let-stakeholder>
<ng-container
*ngTemplateOutlet="stakeholderBox; context: {stakeholder:stakeholder}"></ng-container>
</ng-template>
<div *ngIf="!loading && isCurator()">
<ng-container
*ngTemplateOutlet="newBox; context: {text:'Create a new default profile.', isDefault:true}"></ng-container>
</div>
</div>
</div>
<div *ngIf="!isManager()" class="message">
<h4 class="uk-text-center">
No profiles to manage yet
</h4>
</div>
<div *ngIf="tab != 'templates' && isManager()" class="uk-section">
<div class="uk-flex uk-flex-middle uk-flex-between uk-margin-small-bottom">
<h4 class="uk-margin-remove">Profiles</h4>
<paging-no-load *ngIf="displayStakeholders?.length > pageSize"
(pageChange)="updateCurrentPage($event)"
[currentPage]="currentPage" [size]="pageSize"
[totalResults]="displayStakeholders.length">
</paging-no-load>
</div>
<div class="uk-grid uk-grid-match uk-child-width-1-3@l uk-child-width-1-2@m uk-child-width-1-1"
uk-grid>
<ng-template ngFor
[ngForOf]="displayStakeholders.slice((currentPage-1)*pageSize, currentPage*pageSize)"
let-stakeholder>
<ng-container
*ngTemplateOutlet="stakeholderBox; context: {stakeholder:stakeholder}"></ng-container>
</ng-template>
<div *ngIf="!loading && isCurator()">
<ng-container *ngTemplateOutlet="newBox; context: {text:'Create a new profile by selecting the type ('+typesAsString+') and ' +
'select indicators based on a default or a blank profile.', isDefault:false}"></ng-container>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<ng-template #stakeholderBox let-stakeholder="stakeholder">
<div *ngIf="stakeholder">
<div class="uk-card uk-card-default uk-card-body uk-position-relative" [ngClass]="stakeholder.type">
<div class="uk-position-top-right uk-margin-small-right uk-margin-small-top">
<a class="uk-link-reset uk-flex uk-flex-middle">
<icon [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(stakeholder.visibility)" ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element class="uk-dropdown" uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0;">
<ul class="uk-nav uk-dropdown-nav">
<li>
<a (click)="editStakeholder(stakeholder, !stakeholder.defaultId); hide(element)">Edit</a>
</li>
<li *ngIf="isCurator">
<a (click)="createReport(stakeholder);hide(element)">Cache Indicators</a>
</li>
<li class="uk-nav-divider"></li>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibility" let-v>
<li [class.uk-active]="stakeholder.visibility === v.value">
<a (click)="changeStakeholderStatus(stakeholder, v.value);hide(element)">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" [name]="v.icon" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">{{v.label}}</span>
<icon *ngIf="stakeholder.visibility === v.value" [flex]="true" name="done" class="uk-text-secondary" ratio="0.8"></icon>
</div>
<div *ngIf="stakeholder">
<div class="uk-card uk-card-default uk-card-body uk-position-relative" [ngClass]="stakeholder.type">
<div class="uk-position-top-right uk-margin-small-right uk-margin-small-top">
<a class="uk-link-reset uk-flex uk-flex-middle">
<icon *ngIf="showVisibility" [flex]="true"
[name]="stakeholderUtils.visibilityIcon.get(stakeholder.visibility)" ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
</li>
</ng-template>
<hr *ngIf="isProfileManager(stakeholder)" class="uk-nav-divider">
<li *ngIf="isProfileManager(stakeholder)"><a
(click)="deleteStakeholderOpen(stakeholder);hide(element)">Delete</a>
</li>
</ul>
<div #element class="uk-dropdown"
uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0;">
<ul class="uk-nav uk-dropdown-nav">
<li>
<a (click)="editStakeholder(stakeholder, !stakeholder.defaultId); hide(element)">Edit</a>
</li>
<li *ngIf="isCurator && stakeholderUtils.isCachingIndicators">
<a (click)="createReport(stakeholder);hide(element)">Cache Indicators</a>
</li>
<li *ngIf="showVisibility" class="uk-nav-divider"></li>
<ng-template *ngIf="showVisibility" ngFor [ngForOf]="stakeholderUtils.visibilities" let-v>
<li [class.uk-active]="stakeholder.visibility === v.value">
<a (click)="changeStakeholderStatus(stakeholder, v.value);hide(element)">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" [name]="v.icon" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">{{v.label}}</span>
<icon *ngIf="stakeholder.visibility === v.value" [flex]="true" name="done"
class="uk-text-secondary" ratio="0.8"></icon>
</div>
</a>
</li>
</ng-template>
<hr *ngIf="isProfileManager(stakeholder)" class="uk-nav-divider">
<li *ngIf="isProfileManager(stakeholder)"><a
(click)="deleteStakeholderOpen(stakeholder);hide(element)">Delete</a>
</li>
</ul>
</div>
</div>
<a class="uk-display-block uk-text-center uk-link-reset" [routerLink]="'/admin/' + stakeholder.alias">
<div class="titleContainer uk-h6 uk-margin-remove-bottom uk-margin-top multi-line-ellipsis lines-2">
<p *ngIf="stakeholder.name" class="uk-margin-remove">
{{stakeholder.name}}
</p>
</div>
<div class="logoContainer uk-margin-top uk-flex uk-flex-column uk-flex-center uk-flex-middle">
<img [src]="stakeholder | logoUrl" class="uk-blend-multiply" style="max-height: 80px;">
</div>
</a>
</div>
</div>
<a class="uk-display-block uk-text-center uk-link-reset" [routerLink]="'/admin/' + stakeholder.alias">
<div class="titleContainer uk-h6 uk-margin-remove-bottom uk-margin-top multi-line-ellipsis lines-2">
<p *ngIf="stakeholder.name" class="uk-margin-remove">
{{stakeholder.name}}
</p>
</div>
<div class="logoContainer uk-margin-top uk-flex uk-flex-column uk-flex-center uk-flex-middle">
<img [src]="stakeholder | logoUrl" class="uk-blend-multiply" style="max-height: 80px;">
</div>
</a>
</div>
</div>
</ng-template>
<ng-template #newBox let-text="text" let-isDefault="isDefault">
<ng-container *ngIf="!loading">
<div class="uk-card uk-card-default uk-text-center uk-card-body clickable" (click)="editStakeholder(null, isDefault)">
<div class="uk-text-small uk-text-muted">
{{text}}
</div>
<div class="uk-margin-medium-top uk-margin-small-bottom">
<ng-container *ngIf="!loading">
<div class="uk-card uk-card-default uk-text-center uk-card-body clickable"
(click)="editStakeholder(null, isDefault)">
<div class="uk-text-small uk-text-muted">
{{text}}
</div>
<div class="uk-margin-medium-top uk-margin-small-bottom">
<span class="uk-text-secondary">
<icon name="add" [ratio]="3"></icon>
</span>
</div>
</div>
</ng-container>
</div>
</div>
</ng-container>
</ng-template>
<modal-alert #editStakeholderModal [large]="true" classTitle="uk-background-primary uk-light"
(alertOutput)="editStakeholderComponent.save(callback)" (cancelOutput)="editStakeholderComponent.removePhoto()"
[okDisabled]="editStakeholderComponent.disabled">
<div class="uk-height-large uk-position-relative" *ngIf="editStakeholderComponent.loading">
<loading class="uk-position-center"></loading>
</div>
<div class="uk-padding" [class.uk-hidden]="editStakeholderComponent.loading">
<edit-stakeholder #editStakeholderComponent></edit-stakeholder>
</div>
(alertOutput)="editStakeholderComponent.save(callback)"
(cancelOutput)="editStakeholderComponent.removePhoto()"
[okDisabled]="editStakeholderComponent.disabled">
<div class="uk-height-large uk-position-relative" *ngIf="editStakeholderComponent.loading">
<loading class="uk-position-center"></loading>
</div>
<div class="uk-padding" [class.uk-hidden]="editStakeholderComponent.loading">
<edit-stakeholder #editStakeholderComponent></edit-stakeholder>
</div>
</modal-alert>
<modal-alert #deleteStakeholderModal [overflowBody]="false" (alertOutput)="deleteStakeholder()">
<div class="uk-height-medium uk-position-relative" *ngIf="deleteLoading">
<loading class="uk-position-center"></loading>
</div>
<div *ngIf="!deleteLoading">
This stakeholder will permanently be deleted. Are you sure you want to proceed?
</div>
<div class="uk-height-medium uk-position-relative" *ngIf="deleteLoading">
<loading class="uk-position-center"></loading>
</div>
<div *ngIf="!deleteLoading">
This stakeholder will permanently be deleted. Are you sure you want to proceed?
</div>
</modal-alert>

View File

@ -1,7 +1,8 @@
@import (reference) "~src/assets/openaire-theme/less/color.less";
@import (optional) "~src/assets/extend-theme/less/color.less";
.setType(@color) {
border-bottom: 4px solid fade(@color, 30%);
border-bottom: 4px solid if(@color = none, none, fade(@color, 30%));
& .type {
color: @color;
@ -20,4 +21,12 @@
&.organization {
.setType(@organization-color);
}
&.country {
.setType(@country-color);
}
&.datasource {
.setType(@datasource-color);
}
}

View File

@ -1,9 +1,7 @@
import {Component, OnDestroy, OnInit, ViewChild} from "@angular/core";
import {StakeholderService} from "../../monitor/services/stakeholder.service";
import {EnvProperties} from "../../utils/properties/env-properties";
import {Stakeholder, StakeholderEntities, Visibility} from "../../monitor/entities/stakeholder";
import {Subscriber, zip} from "rxjs";
import {StakeholderUtils} from "../utils/indicator-utils";
import {Stakeholder, Visibility} from "../../monitor/entities/stakeholder";
import {zip} from "rxjs";
import {UntypedFormBuilder, UntypedFormGroup} from "@angular/forms";
import {AlertModal} from "../../utils/modal/alert";
import {Option} from "../../sharedComponents/input/input.component";
@ -11,10 +9,10 @@ import {Title} from "@angular/platform-browser";
import {UserManagementService} from "../../services/user-management.service";
import {Session} from "../../login/utils/helper.class";
import {EditStakeholderComponent} from "../general/edit-stakeholder/edit-stakeholder.component";
import {properties} from "src/environments/environment";
import {ActivatedRoute} from "@angular/router";
import {CacheIndicatorsService} from "../utils/cache-indicators/cache-indicators.service";
import {NotificationHandler} from "../../utils/notification-handler";
import {StakeholderBaseComponent} from "../utils/stakeholder-base.component";
type Tab = 'all' | 'templates'| 'profiles';
@ -25,12 +23,9 @@ declare var UIkit;
templateUrl: "./manageStakeholders.component.html",
styleUrls: ["./manageStakeholders.component.less"]
})
export class ManageStakeholdersComponent implements OnInit, OnDestroy {
public properties: EnvProperties;
export class ManageStakeholdersComponent extends StakeholderBaseComponent implements OnInit, OnDestroy {
public loading: boolean = true;
public deleteLoading: boolean = false;
public stakeholderUtils: StakeholderUtils = new StakeholderUtils();
public defaultStakeholders: Stakeholder[];
public stakeholders: Stakeholder[];
public alias: string[];
@ -38,26 +33,24 @@ export class ManageStakeholdersComponent implements OnInit, OnDestroy {
public index: number;
public user = null;
public tab: Tab = 'all';
public currentPage: number = 1;
public currentTemplatesPage: number = 1;
public pageSize: number = 15;
public typeOptions: Option[];
/**
* Filtered Stakeholders
*/
public displayDefaultStakeholders: Stakeholder[];
public displayStakeholders: Stakeholder[];
public displayDefaultStakeholders: Stakeholder[] = [];
public displayStakeholders: Stakeholder[] = [];
/**
* Top filters
*/
public filters: UntypedFormGroup;
public all: Option = {
value: 'all',
label: 'All'
};
public callback: Function;
/**
* Grid or List View
*/
private subscriptions: any[] = [];
@ViewChild('editStakeholderModal', { static: true }) editStakeholderModal: AlertModal;
@ViewChild('deleteStakeholderModal', { static: true }) deleteStakeholderModal: AlertModal;
@ViewChild('editStakeholderComponent', { static: true }) editStakeholderComponent: EditStakeholderComponent;
@ -65,15 +58,17 @@ export class ManageStakeholdersComponent implements OnInit, OnDestroy {
constructor(private stakeholderService: StakeholderService,
private cacheIndicatorsService: CacheIndicatorsService,
private userManagementService: UserManagementService,
private route: ActivatedRoute,
private title: Title,
protected _route: ActivatedRoute,
protected _title: Title,
private fb: UntypedFormBuilder) {
super();
}
ngOnInit(): void {
this.typeOptions = [{value: 'all', label: 'All'}].concat(this.stakeholderUtils.types);
this.buildFilters();
this.properties = properties;
this.title.setTitle('Manage profiles');
this.title = 'Manage Profiles';
this.setMetadata();
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
this.user = user;
}));
@ -94,45 +89,28 @@ export class ManageStakeholdersComponent implements OnInit, OnDestroy {
}));
}
ngOnDestroy(): void {
this.subscriptions.forEach(value => {
if (value instanceof Subscriber) {
value.unsubscribe();
} else if (value instanceof Function) {
value();
}
});
}
hide(element: any) {
UIkit.dropdown(element).hide();
}
private buildFilters() {
this.filters = this.fb.group({
status: this.fb.control('all'),
type: this.fb.control('all'),
keyword: this.fb.control('')
});
this.subscriptions.push(this.filters.get('status').valueChanges.subscribe(value => {
this.onStatusChange(value);
this.filtering();
}));
this.subscriptions.push(this.filters.get('type').valueChanges.subscribe(value => {
this.filtering();
}));
this.subscriptions.push(this.filters.get('keyword').valueChanges.subscribe(value => {
this.onKeywordChange(value);
this.filtering();
}));
}
onStatusChange(value) {
this.displayDefaultStakeholders = this.filterStatus(this.defaultStakeholders, value);
this.displayStakeholders = this.filterStatus(this.stakeholders, value);
}
onKeywordChange(value) {
this.displayDefaultStakeholders = this.filterByKeyword(this.defaultStakeholders, value);
this.displayStakeholders = this.filterByKeyword(this.stakeholders, value);
}
private filterStatus(stakeholders: Stakeholder[], value): Stakeholder[] {
private filterByStatus(stakeholders: Stakeholder[], value): Stakeholder[] {
if (value === 'all') {
return stakeholders;
} else {
@ -140,6 +118,14 @@ export class ManageStakeholdersComponent implements OnInit, OnDestroy {
}
}
private filterByType(stakeholders: Stakeholder[], value) {
if(value == 'all') {
return stakeholders;
} else {
return stakeholders.filter(item => item.type == value);
}
}
private filterByKeyword(stakeholders: Stakeholder[], value): Stakeholder[] {
if (!value) {
return stakeholders;
@ -154,6 +140,26 @@ export class ManageStakeholdersComponent implements OnInit, OnDestroy {
}
}
filtering() {
let keyword = this.filters.get('keyword').value;
let type = this.filters.get('type').value;
let status = this.filters.get('status').value;
let displayStakeholders = this.stakeholders;
let displayDefaultStakeholders = this.defaultStakeholders;
displayStakeholders = this.filterByKeyword(displayStakeholders, keyword);
displayStakeholders = this.filterByType(displayStakeholders, type);
displayStakeholders = this.filterByStatus(displayStakeholders, status);
displayDefaultStakeholders = this.filterByKeyword(displayDefaultStakeholders, keyword);
displayDefaultStakeholders = this.filterByType(displayDefaultStakeholders, type);
displayDefaultStakeholders = this.filterByStatus(displayDefaultStakeholders, status);
this.displayStakeholders = displayStakeholders;
this.displayDefaultStakeholders = displayDefaultStakeholders;
this.currentPage = 1;
this.currentTemplatesPage = 1;
}
public editStakeholder(stakeholder: Stakeholder = null, isDefault: boolean = false) {
if (isDefault) {
this.index = (stakeholder) ? this.defaultStakeholders.findIndex(value => value._id === stakeholder._id) : -1;
@ -193,6 +199,7 @@ export class ManageStakeholdersComponent implements OnInit, OnDestroy {
}
this.alias.push(stakeholder.alias);
this.editStakeholderModal.cancel();
this.filtering();
};
this.editStakeholderModal.alertTitle = 'Create a new ' + (isDefault?'Default ':'') + 'Profile';
this.editStakeholderModal.okButtonText = 'Create';
@ -244,6 +251,7 @@ export class ManageStakeholdersComponent implements OnInit, OnDestroy {
this.alias = this.alias.filter(item => item !== this.stakeholder.alias);
this.deleteLoading = false;
this.deleteStakeholderModal.cancel();
this.filtering();
}, error => {
UIkit.notification('An error has occurred. Please try again later', {
status: 'danger',
@ -296,16 +304,11 @@ export class ManageStakeholdersComponent implements OnInit, OnDestroy {
' or ' + this.stakeholderUtils.types[this.stakeholderUtils.types.length - 1].label
}
private isTab(tab: Tab): boolean {
switch (tab) {
case "all":
return true;
case "profiles":
return true;
case "templates":
return true;
default:
return false;
}
public updateCurrentPage($event) {
this.currentPage = $event.value;
}
public updateCurrentTemplatesPage($event) {
this.currentTemplatesPage = $event.value;
}
}

View File

@ -19,6 +19,7 @@ import {
} from "../../dashboard/sharedComponents/sidebar/sidebar-mobile-toggle/sidebar-mobile-toggle.module";
import {SliderTabsModule} from "../../sharedComponents/tabs/slider-tabs.module";
import {EditStakeholderModule} from "../general/edit-stakeholder/edit-stakeholder.module";
import {PagingModule} from "../../utils/paging.module";
@NgModule({
declarations: [ManageStakeholdersComponent],
@ -36,7 +37,8 @@ import {EditStakeholderModule} from "../general/edit-stakeholder/edit-stakeholde
LogoUrlPipeModule,
SearchInputModule,
SidebarMobileToggleModule,
SliderTabsModule
SliderTabsModule,
PagingModule
],
providers: [
PreviousRouteRecorder,

View File

@ -39,26 +39,28 @@
<div *ngIf="!dragging"
class="uk-position-top-right uk-margin-small-right uk-margin-small-top">
<a class="uk-link-reset uk-flex uk-flex-middle" [class.uk-disabled]="editing">
<icon [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)" ratio="0.6"></icon>
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)" ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element class="uk-dropdown" uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0">
<ul class="uk-nav uk-dropdown-nav">
<ng-container *ngIf="isCurator">
<li><a (click)="editNumberIndicatorOpen(number, indicator._id); hide(element)">Edit</a></li>
<li class="uk-nav-divider"></li>
</ng-container>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibility" let-v>
<li>
<a (click)="changeIndicatorStatus(number._id, indicator, v.value);hide(element)">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" [name]="v.icon" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">{{v.label}}</span>
<icon *ngIf="indicator.visibility === v.value" [flex]="true" name="done" class="uk-text-secondary" ratio="0.8"></icon>
</div>
</a>
</li>
</ng-template>
<ng-container *ngIf="showVisibility">
<li *ngIf="isCurator" class="uk-nav-divider"></li>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibilities" let-v>
<li>
<a (click)="changeIndicatorStatus(number._id, indicator, v.value);hide(element)">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" [name]="v.icon" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">{{v.label}}</span>
<icon *ngIf="indicator.visibility === v.value" [flex]="true" name="done" class="uk-text-secondary" ratio="0.8"></icon>
</div>
</a>
</li>
</ng-template>
</ng-container>
<ng-container *ngIf="!indicator.defaultId && !editing && isCurator">
<li class="uk-nav-divider">
<li><a (click)="deleteIndicatorOpen(number, indicator._id, 'number', 'delete');hide(element)">Delete</a></li>
@ -136,26 +138,28 @@
<div *ngIf="!dragging"
class="uk-position-top-right uk-margin-small-right uk-margin-small-top">
<a class="uk-link-reset uk-flex uk-flex-middle" [class.uk-disabled]="editing">
<icon [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)" ratio="0.6"></icon>
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)" ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element class="uk-dropdown" uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0">
<ul class="uk-nav uk-dropdown-nav">
<ng-container *ngIf="isCurator">
<li><a (click)="editChartIndicatorOpen(chart, indicator._id); hide(element)">Edit</a></li>
<li class="uk-nav-divider"></li>
</ng-container>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibility" let-v>
<li>
<a (click)="changeIndicatorStatus(chart._id, indicator, v.value);">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" [name]="v.icon" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">{{v.label}}</span>
<icon *ngIf="indicator.visibility === v.value" [flex]="true" name="done" class="uk-text-secondary" ratio="0.8"></icon>
</div>
</a>
</li>
</ng-template>
<ng-container *ngIf="showVisibility">
<li *ngIf="isCurator" class="uk-nav-divider"></li>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibilities" let-v>
<li>
<a (click)="changeIndicatorStatus(chart._id, indicator, v.value);">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" [name]="v.icon" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">{{v.label}}</span>
<icon *ngIf="indicator.visibility === v.value" [flex]="true" name="done" class="uk-text-secondary" ratio="0.8"></icon>
</div>
</a>
</li>
</ng-template>
</ng-container>
<ng-container *ngIf="!indicator.defaultId && !editing && isCurator">
<li class="uk-nav-divider">
<li><a (click)="deleteIndicatorOpen(chart, indicator._id, 'chart', 'delete');hide(element)">Delete</a></li>
@ -231,8 +235,8 @@
<div input class="uk-width-1-1" *ngIf="stakeholder.defaultId" [formInput]="numberIndicatorFb.get('additionalDescription')"
placeholder="Description" type="textarea">
</div>
<div input class="uk-width-1-2@m" [formInput]="numberIndicatorFb.get('visibility')"
placeholder="Visibility" [options]="stakeholderUtils.visibility" type="select">
<div *ngIf="showVisibility" input class="uk-width-1-2@m" [formInput]="numberIndicatorFb.get('visibility')"
placeholder="Visibility" [options]="stakeholderUtils.visibilities" type="select">
</div>
<div input class="uk-width-1-2@m" [formInput]="numberIndicatorFb.get('width')"
placeholder="Number Size" [options]="indicatorUtils.indicatorSizes" type="select">
@ -346,8 +350,8 @@
<div *ngIf="stakeholder.defaultId" input class="uk-width-1-1" [formInput]="chartIndicatorFb.get('additionalDescription')"
placeholder="Description" type="textarea">
</div>
<div input class="uk-width-1-2@m" [formInput]="chartIndicatorFb.get('visibility')"
placeholder="Status" [options]="stakeholderUtils.visibility" type="select">
<div *ngIf="showVisibility" input class="uk-width-1-2@m" [formInput]="chartIndicatorFb.get('visibility')"
placeholder="Status" [options]="stakeholderUtils.visibilities" type="select">
</div>
<div input class="uk-width-1-2@m" [formInput]="chartIndicatorFb.get('width')" placeholder="Chart width"
[options]="indicatorUtils.indicatorSizes" type="select">

View File

@ -5,7 +5,6 @@ import {
HostListener,
Input,
OnChanges,
OnDestroy,
OnInit,
SimpleChanges,
ViewChild
@ -20,7 +19,6 @@ import {
Stakeholder,
Visibility
} from "../../monitor/entities/stakeholder";
import {IndicatorUtils, StakeholderUtils} from "../utils/indicator-utils";
import {
AbstractControl,
UntypedFormArray,
@ -34,7 +32,6 @@ import {StatisticsService} from "../utils/services/statistics.service";
import {HelperFunctions} from "../../utils/HelperFunctions.class";
import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser";
import {Reorder, StakeholderService} from "../../monitor/services/stakeholder.service";
import {EnvProperties} from "../../utils/properties/env-properties";
import {Observable, Subscriber} from "rxjs";
import {LayoutService} from "../../dashboard/sharedComponents/sidebar/layout.service";
import {Router} from "@angular/router";
@ -44,8 +41,9 @@ import {Notification} from "../../notifications/notifications";
import {NotificationUtils} from "../../notifications/notification-utils";
import {NotifyFormComponent} from "../../notifications/notify-form/notify-form.component";
import {NotificationService} from "../../notifications/notification.service";
import {properties} from "src/environments/environment";
import {NotificationHandler} from "../../utils/notification-handler";
import {IndicatorStakeholderBaseComponent} from "../utils/stakeholder-base.component";
import {properties} from "../../../../environments/environment";
declare var UIkit;
declare var copy;
@ -54,10 +52,9 @@ declare var copy;
selector: 'indicators',
templateUrl: './indicators.component.html'
})
export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit {
export class IndicatorsComponent extends IndicatorStakeholderBaseComponent implements OnInit, OnChanges, AfterViewInit {
filesToUpload: Array<File>;
errorMessage = "";
public properties: EnvProperties = properties;
@Input()
public topicIndex: number = 0;
@Input()
@ -71,8 +68,6 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
@Input()
public user: User = null;
public preview: string;
public indicatorUtils: IndicatorUtils = new IndicatorUtils();
public stakeholderUtils: StakeholderUtils = new StakeholderUtils();
public numberIndicatorFb: UntypedFormGroup;
public chartIndicatorFb: UntypedFormGroup;
public chartSections: UntypedFormArray;
@ -118,7 +113,6 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
/**
* Subscriptions
**/
private subscriptions: any[] = [];
private urlSubscriptions: any[] = [];
private numberSubscription: any[] = [];
@ -127,9 +121,10 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
private statisticsService: StatisticsService,
private notificationService: NotificationService,
private fb: UntypedFormBuilder,
private router: Router,
protected _router: Router,
private cdr: ChangeDetectorRef,
private sanitizer: DomSanitizer) {
super()
this.filesToUpload = [];
}
@ -146,13 +141,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
}
ngOnDestroy(): void {
this.subscriptions.forEach(value => {
if (value instanceof Subscriber) {
value.unsubscribe();
} else if (value instanceof Function) {
value();
}
});
super.ngOnDestroy();
this.urlSubscriptions.forEach(value => {
if (value instanceof Subscriber) {
value.unsubscribe();
@ -857,39 +846,42 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.setCharts();
this.setNumbers();
this.initReorder();
this.notification = NotificationUtils.importIndicators(this.user.fullname, this.stakeholder.alias);
this.notification.entity = this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index]._id;
this.notification.name = this.user.firstname;
this.notification.surname = this.user.lastname;
this.notification.stakeholder = this.stakeholder.alias;
this.notification.stakeholderType = this.stakeholder.type;
this.notification.groups = [Role.curator(this.stakeholder.type)];
if (this.stakeholder.defaultId) {
this.notification.groups.push(Role.manager(this.stakeholder.type, this.stakeholder.alias));
this.notificationService.sendNotification(this.notification).subscribe(notification => {
UIkit.notification('A notification has been <b>sent</b> successfully', {
status: 'success',
timeout: 6000,
pos: 'bottom-right'
});
}, error => {
UIkit.notification('An error has occurred. Please try again later', {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
});
} else {
this.stakeholderService.getStakeholders(this.properties.monitorServiceAPIURL, null, this.stakeholder._id).subscribe(stakeholders => {
stakeholders.forEach(value => {
this.notification.groups.push(Role.manager(value.type, value.alias))
});
if(properties.notificationsAPIURL) {
this.notification = NotificationUtils.importIndicators(this.user.fullname, this.stakeholder.alias);
this.notification.entity = this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index]._id;
this.notification.name = this.user.firstname;
this.notification.surname = this.user.lastname;
this.notification.stakeholder = this.stakeholder.alias;
this.notification.stakeholderType = this.stakeholder.type;
this.notification.groups = [Role.curator(this.stakeholder.type)];
if (this.stakeholder.defaultId) {
this.notification.groups.push(Role.manager(this.stakeholder.type, this.stakeholder.alias));
this.notificationService.sendNotification(this.notification).subscribe(notification => {
NotificationHandler.rise('A notification has been <b>sent</b> successfully');
UIkit.notification('A notification has been <b>sent</b> successfully', {
status: 'success',
timeout: 6000,
pos: 'bottom-right'
});
}, error => {
NotificationHandler.rise('An error has occurred. Please try again later', 'danger');
UIkit.notification('An error has occurred. Please try again later', {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
});
});
} else {
this.stakeholderService.getStakeholders(this.properties.monitorServiceAPIURL, null, this.stakeholder._id).subscribe(stakeholders => {
stakeholders.forEach(value => {
this.notification.groups.push(Role.manager(value.type, value.alias))
});
this.notificationService.sendNotification(this.notification).subscribe(notification => {
NotificationHandler.rise('A notification has been <b>sent</b> successfully');
}, error => {
NotificationHandler.rise('An error has occurred. Please try again later', 'danger');
});
});
}
}
this.editing = false;
this.importLoading = false;
@ -1207,7 +1199,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
}
private checkForSchemaEnhancements(url: string) {
this.showCheckForSchemaEnhancements = this.isAdministrator && url && !this.properties.useOldStatisticsSchema && this.indicatorUtils.checkForSchemaEnhancements(url);
this.showCheckForSchemaEnhancements = this.isAdministrator && url && !this.properties.useOldStatisticsSchema && this.indicatorUtils.checkForSchemaEnhancements(url) && this.properties.dashboard != 'irish';
}
migrateFromOldImportJsonFile(charts) {
@ -1237,6 +1229,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
let duplicates = 0;
charts = this.migrateFromOldImportJsonFile(charts);
for (let chart of charts) {
chart.visibility = this.showVisibility?chart.visibility:this.stakeholderUtils.defaultValue(this.stakeholderUtils.visibilities);
if (!sectionsToSave[chart['sectionIndex']]) {
let sectionToSave = new Section(chart['sectionType'] ? chart['sectionType'] : chart['type'], chart['sectionTitle']);
sectionToSave.indicators = [];
@ -1295,7 +1288,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
noValidParams++;
}
if (!exists) {
let i: Indicator = new Indicator(chart.name, chart.description, chart.additionalDescription, chart.type, chart.width, chart.height, "RESTRICTED", [indicatorPath]);
let i: Indicator = new Indicator(chart.name, chart.description, chart.additionalDescription, chart.type, chart.width, chart.height, this.showVisibility?"RESTRICTED":this.stakeholderUtils.defaultValue(this.stakeholderUtils.visibilities), [indicatorPath]);
sectionsToSave[chart['sectionIndex']].indicators.push(i);
countIndicators++;
}
@ -1309,7 +1302,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
pos: 'bottom-right'
});
}
if (noValidParams > 0) {
if (noValidParams > 0 && !(this.stakeholder.type == 'country')) {
let noValidMessage = "Some indicators couldn't be generated properly. Please make sure chart data is for the current stakeholder.";
if (this.stakeholder.defaultId == null) {
noValidMessage = "Some indicators couldn't be generated properly. Stakeholders based on this profile may not inherit the data correctly.";
@ -1379,7 +1372,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
let topic = this.stakeholder ? this.stakeholder.topics[this.topicIndex] : null;
let category = topic ? topic.categories[this.categoryIndex] : null;
let subCategory = category ? category.subCategories[this.subcategoryIndex] : null;
let subCategory = category ? category.subCategories[subcategoryIndex] : null;
var jsonFileUrl = window.URL.createObjectURL(new Blob([JSON.stringify(indicators)], {type: 'application/json'}));
var a = window.document.createElement('a');

View File

@ -9,6 +9,9 @@
<span class="uk-width-expand uk-text-truncate uk-margin-left hide-on-close">Indicators</span>
</a>
</div>
<div *ngIf="showLogo" id="sidebar_logo" class="uk-margin-top">
<img [src]="stakeholder | logoUrl">
</div>
<div class="menu_section uk-margin-large-top">
<ul class="uk-list uk-nav uk-nav-default" transition-group [id]="'topics'"
uk-nav="duration: 400">
@ -26,12 +29,11 @@
<span class="uk-margin-xsmall-left hide-on-close" [class.uk-invisible-hover]="topicIndex !== i"
(click)="$event.stopPropagation();$event.preventDefault()">
<a class="uk-link-reset uk-flex uk-flex-middle">
<icon [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(topic.visibility)"
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(topic.visibility)"
ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element
uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0; flip: false; container: body">
<div #element uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0; flip: false; container: body">
<ul class="uk-nav uk-dropdown-nav">
<ng-container *ngIf="isCurator">
<li>
@ -59,9 +61,10 @@
</div>
</a>
</li>
<li class="uk-nav-divider"></li>
</ng-container>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibility" let-v>
<ng-container *ngIf="showVisibility">
<li *ngIf="isCurator" class="uk-nav-divider"></li>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibilities" let-v>
<li [class.uk-active]="topic.visibility === v.value">
<a (click)="openVisibilityModal(i, v.value, 'topic'); hide(element)">
<div class="uk-flex uk-flex-middle">
@ -73,6 +76,7 @@
</a>
</li>
</ng-template>
</ng-container>
<ng-container *ngIf="!topic.defaultId && isCurator">
<li class="uk-nav-divider">
<li>
@ -103,7 +107,7 @@
<span class="uk-margin-xsmall-left hide-on-close" [class.uk-invisible-hover]="categoryIndex !== j"
(click)="$event.stopPropagation();$event.preventDefault()">
<a class="uk-link-reset uk-flex uk-flex-middle">
<icon [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(category.visibility)"
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(category.visibility)"
ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
@ -137,9 +141,11 @@
</div>
</a>
</li>
<li class="uk-nav-divider"></li>
</ng-container>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibility" let-v>
<ng-container *ngIf="showVisibility">
<li *ngIf="isCurator" class="uk-nav-divider"></li>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibilities" let-v>
<li [class.uk-active]="category.visibility === v.value">
<a (click)="openVisibilityModal(j, v.value, 'category'); hide(element)">
<div class="uk-flex uk-flex-middle">
@ -151,6 +157,7 @@
</a>
</li>
</ng-template>
</ng-container>
<ng-container *ngIf="!category.defaultId && isCurator">
<li class="uk-nav-divider">
<li>
@ -191,8 +198,8 @@
</div>
</aside>
<div #pageContent *ngIf="stakeholder && filters" class="uk-width-1-1" page-content>
<div actions>
<div *ngIf="stakeholder.topics.length > 0" class="uk-flex uk-flex-center uk-margin-medium-top uk-flex-right@m uk-width-1-1">
<div actions class="uk-margin-medium-top">
<div *ngIf="stakeholder.topics.length > 0 && showVisibility" class="uk-flex uk-flex-center uk-flex-right@m uk-width-1-1">
<button class="uk-button uk-button-primary uk-flex uk-flex-middle">
<icon name="visibility" [flex]="true"></icon>
<span class="uk-margin-small-left uk-margin-small-right">Preview</span>
@ -229,7 +236,7 @@
[class.uk-invisible-hover]="subCategoryIndex !== i"
(click)="$event.stopPropagation();$event.preventDefault()">
<a class="uk-link-reset uk-flex uk-flex-middle">
<icon [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(subCategory.visibility)"
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(subCategory.visibility)"
ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
@ -279,9 +286,10 @@
</div>
</a>
</li>
<li class="uk-nav-divider"></li>
</ng-container>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibility" let-v>
<ng-container *ngIf="showVisibility">
<li *ngIf="isCurator" class="uk-nav-divider"></li>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibilities" let-v>
<li [class.uk-active]="subCategory.visibility === v.value">
<a (click)="openVisibilityModal(i, v.value, 'subcategory'); hide(element)">
<div class="uk-flex uk-flex-middle">
@ -293,6 +301,7 @@
</a>
</li>
</ng-template>
</ng-container>
<ng-container *ngIf="!subCategory.defaultId && isCurator">
<li class="uk-nav-divider">
<li>
@ -348,8 +357,8 @@
<div *ngIf="form" [class.uk-hidden]="loading"
class="uk-grid uk-padding uk-padding-remove-horizontal uk-child-width-1-1" [formGroup]="form" uk-grid>
<div input [formInput]="form.get('name')" class="uk-width-1-2@m" placeholder="Title"></div>
<div input [formInput]="form.get('visibility')" class="uk-width-1-2@m" placeholder="Status"
[options]="stakeholderUtils.visibility" type="select"></div>
<div *ngIf="showVisibility" input [formInput]="form.get('visibility')" class="uk-width-1-2@m" placeholder="Status"
[options]="stakeholderUtils.visibilities" type="select"></div>
<div input [formInput]="form.get('description')" placeholder="Description" type="textarea" rows="4"></div>
<div *ngIf="form.get('icon')" input [formInput]="form.get('icon')" placeholder="Icon(SVG)" type="textarea"></div>
</div>

View File

@ -1,32 +1,32 @@
import {
AfterViewInit,
ChangeDetectorRef,
Component, Inject,
Component,
Inject,
OnDestroy,
OnInit, PLATFORM_ID,
OnInit,
PLATFORM_ID,
QueryList,
ViewChild,
ViewChildren
} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {Title} from '@angular/platform-browser';
import {EnvProperties} from '../../utils/properties/env-properties';
import {Category, Stakeholder, SubCategory, Topic, Visibility} from "../../monitor/entities/stakeholder";
import {StakeholderService} from "../../monitor/services/stakeholder.service";
import {HelperFunctions} from "../../utils/HelperFunctions.class";
import {AlertModal} from "../../utils/modal/alert";
import {BehaviorSubject, Subject, Subscriber, Subscription} from "rxjs";
import {UntypedFormBuilder, UntypedFormGroup, Validators} from "@angular/forms";
import {StakeholderUtils} from "../utils/indicator-utils";
import {StringUtils} from "../../utils/string-utils.class";
import {IDeactivateComponent} from "../../utils/can-exit.guard";
import {LayoutService} from "../../dashboard/sharedComponents/sidebar/layout.service";
import {Option} from "../../sharedComponents/input/input.component";
import {properties} from "src/environments/environment";
import {Session, User} from "../../login/utils/helper.class";
import {UserManagementService} from "../../services/user-management.service";
import {TransitionGroupComponent} from "../../utils/transition-group/transition-group.component";
import {NotificationHandler} from "../../utils/notification-handler";
import {StakeholderBaseComponent} from "../utils/stakeholder-base.component";
declare var UIkit;
@ -34,14 +34,12 @@ declare var UIkit;
selector: 'topic',
templateUrl: './topic.component.html',
})
export class TopicComponent implements OnInit, OnDestroy, AfterViewInit, IDeactivateComponent {
export class TopicComponent extends StakeholderBaseComponent implements OnInit, OnDestroy, AfterViewInit, IDeactivateComponent {
private topicSubscriptions: any[] = [];
private subscriptions: any[] = [];
public properties: EnvProperties = properties;
public offset: number;
public stakeholderUtils: StakeholderUtils = new StakeholderUtils();
public loading: boolean = false;
public stakeholder: Stakeholder;
public showLogo: boolean = false;
public user: User;
/**
* Stakeholder change event
@ -84,18 +82,21 @@ export class TopicComponent implements OnInit, OnDestroy, AfterViewInit, IDeacti
};
constructor(
private route: ActivatedRoute,
private router: Router,
private title: Title,
protected _route: ActivatedRoute,
protected _router: Router,
protected _title: Title,
private fb: UntypedFormBuilder,
private stakeholderService: StakeholderService,
private userManagementService: UserManagementService,
private layoutService: LayoutService,
private cdr: ChangeDetectorRef,
protected cdr: ChangeDetectorRef,
@Inject(PLATFORM_ID) private platformId) {
super();
}
public ngOnInit() {
if(this._route.snapshot.data?.showLogo === true) {
this.showLogo = true;
}
if (typeof document !== "undefined") {
this.offset = Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--header-height'));
}
@ -115,7 +116,7 @@ export class TopicComponent implements OnInit, OnDestroy, AfterViewInit, IDeacti
this.subCategoryIndex = index;
});
}));
this.subscriptions.push(this.route.params.subscribe(params => {
this.subscriptions.push(this._route.params.subscribe(params => {
if (subscription) {
subscription.unsubscribe();
}
@ -136,7 +137,8 @@ export class TopicComponent implements OnInit, OnDestroy, AfterViewInit, IDeacti
if (this.topicIndex === -1) {
this.navigateToError();
} else {
this.title.setTitle(stakeholder.name + " | Indicators");
this.title = stakeholder.name + " | Indicators"
this.setMetadata();
}
}
});
@ -163,16 +165,12 @@ export class TopicComponent implements OnInit, OnDestroy, AfterViewInit, IDeacti
}
public ngOnDestroy() {
super.ngOnDestroy();
this.topicSubscriptions.forEach(value => {
if (value instanceof Subscriber) {
value.unsubscribe();
}
});
this.subscriptions.forEach(value => {
if (value instanceof Subscriber) {
value.unsubscribe();
}
});
}
canExit(): boolean {
@ -665,10 +663,6 @@ export class TopicComponent implements OnInit, OnDestroy, AfterViewInit, IDeacti
});
}
private navigateToError() {
this.router.navigate([this.properties.errorLink], {queryParams: {'page': this.router.url}});
}
get isCurator(): boolean {
return Session.isPortalAdministrator(this.user) || Session.isCurator(this.stakeholder.type, this.user);
}
@ -714,8 +708,8 @@ export class TopicComponent implements OnInit, OnDestroy, AfterViewInit, IDeacti
this.editModal.cancel();
NotificationHandler.rise(message);
if (redirect) {
this.router.navigate(['../' + saveElement.alias], {
relativeTo: this.route
this._router.navigate(['../' + saveElement.alias], {
relativeTo: this._route
});
}
}, error => {
@ -758,8 +752,8 @@ export class TopicComponent implements OnInit, OnDestroy, AfterViewInit, IDeacti
}
back() {
this.router.navigate(['../'], {
relativeTo: this.route
this._router.navigate(['../'], {
relativeTo: this._route
});
}
@ -775,19 +769,6 @@ export class TopicComponent implements OnInit, OnDestroy, AfterViewInit, IDeacti
}
}
public get isSmallScreen() {
return this.layoutService.isSmallScreen;
}
public get open() {
return this.layoutService.open;
}
public toggleOpen(event: MouseEvent) {
event.preventDefault();
this.layoutService.setOpen(!this.open);
}
public openVisibilityModal(index: number, visibility: Visibility, type: any) {
this.index = index;
this.visibility = visibility;

View File

@ -8,12 +8,14 @@ import {StakeholderService} from "../../monitor/services/stakeholder.service";
import {Observable, zip} from "rxjs";
@Injectable()
@Injectable({
providedIn: 'root'
})
export class AdminDashboardGuard {
constructor(private router: Router,
private stakeholderService: StakeholderService,
private userManagementService: UserManagementService) {
constructor(protected router: Router,
protected stakeholderService: StakeholderService,
protected userManagementService: UserManagementService) {
}
check(path: string, alias: string): Observable<boolean> | boolean {
@ -38,6 +40,6 @@ export class AdminDashboardGuard {
}
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.check(state.url, childRoute.params.stakeholder);
return this.check(state.url, childRoute.data?.stakeholder?childRoute.data.stakeholder:childRoute.params.stakeholder);
}
}

View File

@ -7,11 +7,9 @@ import {
IndicatorPathType,
IndicatorType,
SourceType,
Stakeholder,
StakeholderEntities,
SubCategory,
Topic, stakeholderTypes,
Visibility
Stakeholder, SubCategory,
Topic,
Visibility,
} from "../../monitor/entities/stakeholder";
import {AbstractControl, ValidatorFn, Validators} from "@angular/forms";
import {Option} from "../../sharedComponents/input/input.component";
@ -19,34 +17,85 @@ import {Session} from "../../login/utils/helper.class";
import {HelperFunctions} from "../../utils/HelperFunctions.class";
import {properties} from "src/environments/environment";
export class StakeholderUtils {
class Entities {
stakeholder = 'Dashboard';
funder = 'Funder';
ri = 'Research Initiative';
organization = 'Research Institution';
project = 'Project';
country = 'National';
datasource = 'Repository';
researcher = 'Researcher';
statuses: Option[] = [
{value: 'PUBLIC', label: 'Public'},
{value: 'RESTRICTED', label: 'Restricted'},
{value: 'PRIVATE', label: 'Private'}
stakeholders = 'Dashboards';
funders = 'Funders';
ris = 'Research Initiatives';
organizations = 'Research Institutions';
projects = 'Projects';
datasources = 'Repositories';
researchers = 'Researchers';
}
export class StakeholderConfiguration {
public static ENTITIES: Entities = new Entities();
public static TYPES: Option[] = [
{value: 'funder', label: StakeholderConfiguration.ENTITIES.funder},
{value: 'ri', label: StakeholderConfiguration.ENTITIES.ri},
{value: 'organization', label: StakeholderConfiguration.ENTITIES.organization},
{value: 'project', label: StakeholderConfiguration.ENTITIES.project}
];
types: Option[] = [
...stakeholderTypes
public static LOCALES: Option[] = [
{value: "en", label: 'English'},
{value: "eu", label: 'Europe'}
];
visibility: Option[] = [
public static FUNDER_TYPES: Option[] = [];
public static VISIBILITIES: Option[] = [
{icon: 'earth', value: "PUBLIC", label: 'Public'},
{icon: 'restricted', value: "RESTRICTED", label: 'Restricted'},
{icon: 'incognito', value: "PRIVATE", label: 'Private'},
];
public static CACHE_INDICATORS: boolean = true;
}
locales: Option[] = [
{value: "en", label: 'English'},
{value: "eu", label: 'Europe'}
];
export class StakeholderUtils {
get entities() {
return StakeholderConfiguration.ENTITIES;
}
visibilityIcon: Map<Visibility, string> = new Map<Visibility, string>([
["PUBLIC", 'earth'],
["PRIVATE", 'incognito'],
["RESTRICTED", 'restricted']
]);
get types() {
return StakeholderConfiguration.TYPES
}
get funderTypes() {
return StakeholderConfiguration.FUNDER_TYPES;
}
get locales() {
return StakeholderConfiguration.LOCALES;
}
get visibilities() {
return StakeholderConfiguration.VISIBILITIES;
}
get isCachingIndicators() {
return StakeholderConfiguration.CACHE_INDICATORS;
}
visibilityIcon: Map<Visibility, string> = new Map<Visibility, string>(this.visibilities.map(option => [option.value, option.icon]));
defaultValue(options: Option[]) {
return options.length === 1?options[0].value:null;
}
showField(options: Option[]) {
return options.length > 1;
}
getLabel(options: Option[], value) {
let option = options.find(option => option.value === value);
return option?option.label:null;
}
getTypesByUserRoles(user, id: string = null): Option[] {
let types = [];
@ -116,7 +165,7 @@ export class StakeholderUtils {
aliasValidatorString(elements: string[]): ValidatorFn {
return (control: AbstractControl): { [key: string]: string } | null => {
if (control.value && elements.find(element =>
element === control.value
element === control.value
)) {
return {'error': 'Alias already in use'};
}
@ -127,7 +176,7 @@ export class StakeholderUtils {
aliasValidator(elements: any[]): ValidatorFn {
return (control: AbstractControl): { [key: string]: string } | null => {
if (control.value && elements.find(element =>
element.alias === control.value
element.alias === control.value
)) {
return {'error': 'Alias already in use'};
}
@ -197,16 +246,16 @@ export class IndicatorUtils {
constructor() {
this.numberSources.set('statistics', [properties.statisticsAPIURL]);
this.numberSources.set('search', [properties.searchAPIURLLAst]);
this.numberSources.set('stats-tool', [properties.monitorStatsFrameUrl, "http://marilyn.athenarc.gr:8080/stats-api/", "http://88.197.53.71:8080/stats-api/", "https://stats.madgik.di.uoa.gr/stats-api/","https://beta.services.openaire.eu/stats-tool/","https://services.openaire.eu/stats-tool/","https://services.openaire.eu/monitor-stats-tool/"]);
this.chartSources.set('stats-tool', [properties.monitorStatsFrameUrl, "http://marilyn.athenarc.gr:8080/stats-api/", "http://88.197.53.71:8080/stats-api/", "https://stats.madgik.di.uoa.gr/stats-api/","https://beta.services.openaire.eu/stats-tool/","https://services.openaire.eu/stats-tool/","https://services.openaire.eu/monitor-stats-tool/"]);
this.numberSources.set('stats-tool', [properties.monitorStatsFrameUrl, "http://marilyn.athenarc.gr:8080/stats-api/", "http://88.197.53.71:8080/stats-api/", "https://stats.madgik.di.uoa.gr/stats-api/", "https://beta.services.openaire.eu/stats-tool/", "https://services.openaire.eu/stats-tool/", "https://services.openaire.eu/monitor-stats-tool/"]);
this.chartSources.set('stats-tool', [properties.monitorStatsFrameUrl, "http://marilyn.athenarc.gr:8080/stats-api/", "http://88.197.53.71:8080/stats-api/", "https://stats.madgik.di.uoa.gr/stats-api/", "https://beta.services.openaire.eu/stats-tool/", "https://services.openaire.eu/stats-tool/", "https://services.openaire.eu/monitor-stats-tool/"]);
this.chartSources.set('old', [properties.statisticsFrameAPIURL]);
this.chartSources.set('image', [""]);
}
getSourceType(source:string): SourceType{
getSourceType(source: string): SourceType {
let sourceType: SourceType = 'search';
this.numberSources.forEach((values, key) => {
if(key == source) {
if (key == source) {
sourceType = key;
}
});
@ -343,12 +392,71 @@ export class IndicatorUtils {
return (indicatorPath.chartObject ? indicatorPath.url + encodeURIComponent(replacedUrl) : replacedUrl);
}
public getFullUrlWithFilters(stakeholder: Stakeholder, indicatorPath: IndicatorPath, fundingL0: string = null, startYear: string = null, endYear: string = null, coFunded: boolean = false): string {
public getFullUrlWithFilters(stakeholder: Stakeholder, indicatorPath: IndicatorPath, fundingL0: string = null, startYear: string = null, endYear: string = null, coFunded: boolean = false, foslvl1:string[]=[], foslvl2:string[]=[], publiclyFunded: "all"| "true"| "false"= "all" ): string {
let filterSubtitleText = [];
indicatorPath.filtersApplied = 0;
let replacedUrl = indicatorPath.chartObject ? indicatorPath.chartObject : indicatorPath.url;
if (stakeholder.statsProfile) {
replacedUrl = replacedUrl.split(ChartHelper.prefix + this.statsProfileParameter + ChartHelper.suffix).join(stakeholder.statsProfile);
}
if (fundingL0) {
if (indicatorPath.source == "stats-tool" && indicatorPath.chartObject) {
let filterResults = this.addFilter(replacedUrl, 'fundingL0', fundingL0);
replacedUrl = filterResults.url;
indicatorPath.filtersApplied += filterResults.filtersApplied;
filterSubtitleText.push( "Funding level 0: " ) ;
}
}
if (startYear) {
if (indicatorPath.source == "stats-tool" && indicatorPath.chartObject) {
let filterResults = this.addFilter(replacedUrl, 'start_year', startYear);
replacedUrl = filterResults.url;
indicatorPath.filtersApplied += filterResults.filtersApplied;
}
}
if (endYear) {
if (indicatorPath.source == "stats-tool" && indicatorPath.chartObject) {
let filterResults = this.addFilter(replacedUrl, 'end_year', endYear);
replacedUrl = filterResults.url;
indicatorPath.filtersApplied += filterResults.filtersApplied;
}
}
if(startYear || endYear) {
filterSubtitleText.push(startYear && endYear ? (startYear + ' - ' + endYear) : (endYear ? ('until ' + endYear) : ''));
}
if (coFunded) {
if (indicatorPath.source == "stats-tool" && indicatorPath.chartObject) {
let filterResults = this.addFilter(replacedUrl, 'co-funded', coFunded);
replacedUrl = filterResults.url;
indicatorPath.filtersApplied += filterResults.filtersApplied;
filterSubtitleText.push( "Co-funded: " + (coFunded?'yes':'no') ) ;
}
}
if (publiclyFunded && publiclyFunded !="all") {
if (indicatorPath.source == "stats-tool" && indicatorPath.chartObject) {
let filterResults = this.addFilter(replacedUrl, 'publicly-funded', publiclyFunded);
replacedUrl = filterResults.url;
indicatorPath.filtersApplied += filterResults.filtersApplied;
filterSubtitleText.push( "Publicly funded: " + (publiclyFunded?'yes':'no') ) ;
}
}
if (foslvl1) {
if (indicatorPath.source == "stats-tool" && indicatorPath.chartObject) {
let filterResults = this.addFilter(replacedUrl, 'foslvl1', foslvl1);
replacedUrl = filterResults.url;
indicatorPath.filtersApplied +=filterResults.filtersApplied?foslvl1.length:0;
}
}
if (foslvl2) {
if (indicatorPath.source == "stats-tool" && indicatorPath.chartObject) {
let filterResults = this.addFilter(replacedUrl, 'foslvl2', foslvl2);
replacedUrl = filterResults.url;
indicatorPath.filtersApplied += filterResults.filtersApplied?foslvl2.length:0;
}
}
if((foslvl1 && foslvl1.length > 0) || (foslvl2 && foslvl2.length > 0)){
filterSubtitleText.push( "Field of Science: " + foslvl1.join(', ') + ( foslvl1.length > 0 && foslvl2.length > 0? ', ': '') + foslvl2.join(", ") ) ;
}
if (indicatorPath.parameters) {
Object.keys(indicatorPath.parameters).forEach(key => {
let replacedValue = indicatorPath.parameters[key];
@ -371,39 +479,12 @@ export class IndicatorUtils {
if (key == "index_shortName") {
replacedValue = stakeholder.index_shortName.toLowerCase();
}
if (key == "subtitle" && filterSubtitleText.length > 0) {
replacedValue = replacedValue + (replacedValue.length > 0 ? ' - ':'') + ' Active filters: ('+filterSubtitleText.join(", ") + ')';
}
replacedUrl = replacedUrl.split(ChartHelper.prefix + key + ChartHelper.suffix).join(replacedValue)
});
}
if (fundingL0) {
if (indicatorPath.source == "stats-tool" && indicatorPath.chartObject) {
let filterResults = this.addFilter(replacedUrl, 'fundingL0', fundingL0);
replacedUrl = filterResults.url;
indicatorPath.filtersApplied += filterResults.filtersApplied;
}
}
if (startYear) {
if (indicatorPath.source == "stats-tool" && indicatorPath.chartObject) {
let filterResults = this.addFilter(replacedUrl, 'start_year', startYear);
replacedUrl = filterResults.url;
indicatorPath.filtersApplied += filterResults.filtersApplied;
}
}
if (endYear) {
if (indicatorPath.source == "stats-tool" && indicatorPath.chartObject) {
let filterResults = this.addFilter(replacedUrl, 'end_year', endYear);
replacedUrl = filterResults.url;
indicatorPath.filtersApplied += filterResults.filtersApplied;
}
}
if (coFunded) {
if (indicatorPath.source == "stats-tool" && indicatorPath.chartObject) {
let filterResults = this.addFilter(replacedUrl, 'co-funded', endYear);
replacedUrl = filterResults.url;
indicatorPath.filtersApplied += filterResults.filtersApplied;
}
}
//For numbers
if (replacedUrl.indexOf(ChartHelper.prefix + 'index_id' + ChartHelper.suffix) != -1) {
replacedUrl = replacedUrl.split(ChartHelper.prefix + 'index_id' + ChartHelper.suffix).join(encodeURIComponent(stakeholder.index_id));
@ -414,7 +495,6 @@ export class IndicatorUtils {
if (replacedUrl.indexOf(ChartHelper.prefix + 'index_shortName' + ChartHelper.suffix) != -1) {
replacedUrl = replacedUrl.split(ChartHelper.prefix + 'index_shortName' + ChartHelper.suffix).join(encodeURIComponent(stakeholder.index_shortName));
}
//Check apply enhancements return this.applySchemaEnhancements( ..);
return (indicatorPath.chartObject ? indicatorPath.url + encodeURIComponent(replacedUrl) : replacedUrl);
}
@ -493,7 +573,7 @@ export class IndicatorUtils {
/*Chart with proper json object*/
//apply the filter in any select fields
for (let select of queries["query"]["select"]) {
let filterString = IndicatorFilterUtils.getFilter(select["field"], filterType);
let filterString = IndicatorFilterUtils.getFilter(select["field"], filterType,filterValue);
if (filterString) {
let filter = JSON.parse(filterString);
//check if filter already exists
@ -557,7 +637,7 @@ export class IndicatorUtils {
generateIndicatorByForm(form: any, indicatorPaths: IndicatorPath[], type: IndicatorType, addParameters: boolean = true): Indicator {
let indicator: Indicator = new Indicator(form.name, form.description, form.additionalDescription, type,
form.width, form.height, form.visibility, indicatorPaths, form.defaultId);
form.width, form.height, form.visibility, indicatorPaths, form.defaultId);
indicator._id = form._id;
form.indicatorPaths.forEach((indicatorPath, index) => {
indicator.indicatorPaths[index].type = indicatorPath.type;
@ -703,6 +783,8 @@ export class IndicatorUtils {
this.extractFunder(obj, indicatorPath, stakeholder);
this.extractRI(obj, indicatorPath, stakeholder);
this.extractOrganization(obj, indicatorPath, stakeholder);
this.extractDatasource(obj, indicatorPath, stakeholder);
this.extractResearcher(obj, indicatorPath, stakeholder);
}
private extractFunder(obj, indicatorPath: IndicatorPath, stakeholder: Stakeholder) {
@ -719,15 +801,13 @@ export class IndicatorUtils {
for (let filter of query["query"]["filters"]) {
for (let gfilter of filter["groupFilters"]) {
//ignore field No Of Funders
if (gfilter["field"].indexOf(" funder") != -1 && gfilter["field"].indexOf(" funders") == -1) {//new statistcs schema
gfilter["values"][0] = ChartHelper.prefix + "index_name" + ChartHelper.suffix;
indicatorPath.parameters["index_name"] = stakeholder.index_name;
} else if (gfilter["field"].indexOf(".funder") != -1) {
gfilter["values"][0] = ChartHelper.prefix + "index_name" + ChartHelper.suffix;
indicatorPath.parameters["index_name"] = stakeholder.index_name;
} else if (gfilter["field"].indexOf(".funder.id") != -1) {
gfilter["values"][0] = ChartHelper.prefix + "index_shortName" + ChartHelper.suffix;
indicatorPath.parameters["index_shortName"] = stakeholder.index_shortName;
let replacedValue = this.replaceIndexValues(gfilter["values"][0], stakeholder, indicatorPath.parameters);
if(replacedValue) { // don't proceed in replacement if no replaced value matches
if ((gfilter["field"].indexOf(" funder") != -1 && gfilter["field"].indexOf(" funders") == -1 ) ||
(gfilter["field"].indexOf(".funder") != -1) ||
(gfilter["field"].indexOf(".funder.id") != -1)) {
gfilter["values"][0] = replacedValue;
}
}
}
}
@ -747,12 +827,12 @@ export class IndicatorUtils {
}
for (let filter of query["query"]["filters"]) {
for (let gfilter of filter["groupFilters"]) {
if (gfilter["field"].indexOf(".context.name") != -1) {
gfilter["values"][0] = ChartHelper.prefix + "index_name" + ChartHelper.suffix;
indicatorPath.parameters["index_name"] = stakeholder.index_name;
} else if (gfilter["field"].indexOf(".context.id") != -1) {
gfilter["values"][0] = ChartHelper.prefix + "index_shortName" + ChartHelper.suffix;
indicatorPath.parameters["index_shortName"] = stakeholder.index_shortName;
let replacedValue = this.replaceIndexValues(gfilter["values"][0], stakeholder, indicatorPath.parameters);
if(replacedValue) { // don't proceed in replacement if no replaced value matches
if ((gfilter["field"].indexOf(".context.name") != -1)
|| (gfilter["field"].indexOf(".context.id") != -1)) {
gfilter["values"][0] = replacedValue;
}
}
}
}
@ -774,18 +854,80 @@ export class IndicatorUtils {
}
for (let filter of query["query"]["filters"]) {
for (let gfilter of filter["groupFilters"]) {
if (gfilter["field"].indexOf(".organization.name") != -1) {
gfilter["values"][0] = ChartHelper.prefix + "index_name" + ChartHelper.suffix;
indicatorPath.parameters["index_name"] = stakeholder.index_name;
} else if (gfilter["field"].indexOf(".organization.id") != -1) {
gfilter["values"][0] = ChartHelper.prefix + "index_shortName" + ChartHelper.suffix;
indicatorPath.parameters["index_shortName"] = stakeholder.index_shortName;
let replacedValue = this.replaceIndexValues(gfilter["values"][0], stakeholder, indicatorPath.parameters);
if(replacedValue) { // don't proceed in replacement if no replaced value matches
if ((gfilter["field"].indexOf(".organization.name") != -1)||
(gfilter["field"].indexOf(".organization.id") != -1)) {
gfilter["values"][0] = replacedValue;
}
}
}
}
}
}
private extractDatasource(obj, indicatorPath: IndicatorPath, stakeholder: Stakeholder) {
// works for .datasource.name and .HostedBy datasource
// and .datasource.id
if (stakeholder.type != "datasource") {
return;
}
for (let query of this.getQueryObjectName(obj) ? obj[this.getDescriptionObjectName(obj)][this.getQueryObjectName(obj)] : obj[this.getDescriptionObjectName(obj)]) {
if (query["query"]["profile"]) {
query["query"]["profile"] = ChartHelper.prefix + this.statsProfileParameter + ChartHelper.suffix;
}
if (!query["query"]["filters"]) {
return;
}
for (let filter of query["query"]["filters"]) {
for (let gfilter of filter["groupFilters"]) {
let replacedValue = this.replaceIndexValues(gfilter["values"][0], stakeholder, indicatorPath.parameters);
if(replacedValue) { // don't proceed in replacement if no replaced value matches
if ((gfilter["field"].indexOf(".datasource.name") != -1 || gfilter["field"].indexOf(".HostedBy datasource") != -1)||
(gfilter["field"].indexOf(".datasource.id") != -1) || (gfilter["field"].indexOf(".hostedby") != -1)) {
gfilter["values"][0] = replacedValue;
}
}
}
}
}
}
private extractResearcher(obj, indicatorPath: IndicatorPath, stakeholder: Stakeholder) {
// works for .orcid
if (stakeholder.type != "researcher") {
return;
}
for (let query of this.getQueryObjectName(obj) ? obj[this.getDescriptionObjectName(obj)][this.getQueryObjectName(obj)] : obj[this.getDescriptionObjectName(obj)]) {
if (query["query"]["profile"]) {
query["query"]["profile"] = ChartHelper.prefix + this.statsProfileParameter + ChartHelper.suffix;
}
if (!query["query"]["filters"]) {
return;
}
for (let filter of query["query"]["filters"]) {
for (let gfilter of filter["groupFilters"]) {
let replacedValue = this.replaceIndexValues(gfilter["values"][0], stakeholder, indicatorPath.parameters);
if(replacedValue) { // don't proceed in replacement if no replaced value matches
if ((gfilter["field"].indexOf(".orcid") != -1 )) {
gfilter["values"][0] = replacedValue;
}
}
}
}
}
}
private replaceIndexValues(currentValue, stakeholder, parameters ){
if(currentValue == stakeholder.index_name){
parameters["index_name"] = stakeholder.index_name;
return ChartHelper.prefix + "index_name" + ChartHelper.suffix;
}else if(currentValue == stakeholder.index_id){
parameters["index_id"] = stakeholder.index_id;
return ChartHelper.prefix + "index_id" + ChartHelper.suffix;
}else if(currentValue == stakeholder.index_shortName) {
parameters["index_shortName"] = stakeholder.index_shortName;
return ChartHelper.prefix + "index_shortName" + ChartHelper.suffix;
}
}
private extractStartYear(obj, indicatorPath: IndicatorPath) {
let start_year;
for (let query of obj[this.getDescriptionObjectName(obj)][this.getQueryObjectName(obj)]) {

View File

@ -0,0 +1,54 @@
import {Directive} from "@angular/core";
import {BaseComponent} from "../../sharedComponents/base/base.component";
import {IndicatorUtils, StakeholderUtils} from "./indicator-utils";
import {ConnectHelper} from "../../connect/connectHelper";
@Directive()
export abstract class StakeholderBaseComponent extends BaseComponent {
stakeholderUtils: StakeholderUtils = new StakeholderUtils();
get entities() {
return this.stakeholderUtils.entities;
}
get showVisibility() {
return this.stakeholderUtils.showField(this.stakeholderUtils.visibilities);
}
get showType() {
return this.stakeholderUtils.showField(this.stakeholderUtils.types);
}
get showFunderType() {
return this.stakeholderUtils.showField(this.stakeholderUtils.funderTypes);
}
getFunderTypeLabel(value: any) {
return this.stakeholderUtils.getLabel(this.stakeholderUtils.funderTypes, value);
}
get showLocale() {
return this.stakeholderUtils.showField(this.stakeholderUtils.locales);
}
protected navigateToError() {
this._router.navigate([this.properties.errorLink], {queryParams: {'page': this._router.url}});
}
setProperties(id, type = null, configurationService) {
this.properties.adminToolsCommunity = id;
if (type) {
this.properties.adminToolsPortalType = type;
} else {
ConnectHelper.setPortalTypeFromPid(id);
}
configurationService.initPortal(this.properties, this.properties.adminToolsCommunity);
}
}
@Directive()
export abstract class IndicatorStakeholderBaseComponent extends StakeholderBaseComponent {
indicatorUtils: IndicatorUtils = new IndicatorUtils();
}

View File

@ -1,24 +1,25 @@
import {SafeResourceUrl} from "@angular/platform-browser";
import {properties} from "../../../../environments/environment";
import {Session, User} from "../../login/utils/helper.class";
import {Option} from "../../sharedComponents/input/input.component";
export const ChartHelper = {
prefix: "((__",
suffix: "__))"
};
export type StakeholderType = 'funder' | 'ri' | 'project' | 'organization';
export type StakeholderType = 'funder' | 'ri' | 'project' | 'organization' | 'country' | 'researcher' | 'datasource';
export type IndicatorType = 'number' | 'chart';
export type IndicatorSize = 'small' | 'medium' | 'large';
export type IndicatorPathType = 'table' | 'bar' | 'column' | 'pie' | 'line' | 'other';
export type SourceType = 'statistics' | 'search' |'stats-tool' | 'old' | 'image';
export type Format = 'NUMBER' | 'PERCENTAGE';
export type Visibility = 'PUBLIC' | 'PRIVATE' | 'RESTRICTED';
export type Overlay = 'embed' | 'description' | false;
export class Stakeholder {
_id: string;
type: StakeholderType;
funderType: string;
name: string;
index_id;
index_name: string;
@ -36,8 +37,9 @@ export class Stakeholder {
isUpload: boolean = false;
description: string;
topics: any[];
details?: any;
constructor(_id: string, type: StakeholderType, index_id, index_name: string, index_shortName: string, alias: string, visibility: Visibility, logoUrl: string, defaultId: string = null, description: string = null) {
constructor(_id: string, type: StakeholderType, index_id: string, index_name: string, index_shortName: string, alias: string, visibility: Visibility, logoUrl: string, defaultId: string = null, description: string = null) {
this._id = _id;
this.type = type;
this.index_id = index_id;
@ -170,7 +172,7 @@ export class Indicator {
visibility: Visibility;
defaultId: string;
indicatorPaths: IndicatorPath[];
descriptionOverlay: boolean = false;
overlay: Overlay = false;
constructor(name: string, description: string, additionalDescription:string, type: IndicatorType, width: IndicatorSize,height: IndicatorSize, visibility: Visibility, indicatorPaths: IndicatorPath[], defaultId: string = null) {
this._id = null;
@ -221,66 +223,104 @@ export class IndicatorPath {
}
export type FilterType = "fundingL0"|"start_year" | "end_year" | "co-funded";
export type FilterType = "fundingL0"|"start_year" | "end_year" | "co-funded" | "foslvl1" | "foslvl2" | "publicly-funded";
export class IndicatorFilterUtils {
public static filteredFields = {
"year":{
"result": "result.year",
"indi_pub_downloads_year": "indi_pub_downloads_year.year", //exclude indi_pub_downloads_year.year because it throws errors. when there is a solution remove the exclusion
"indi_pub_downloads":"indi_pub_downloads.result.year",
"result_apc":"result_apc.result.year",
"result_apc_affiliations":"result_apc_affiliations.result.year",
"result_country":"result_country.result.year",
"indi_impact_measures": "indi_impact_measures.result.year",
"project": "project.start year"
},
"fundingL0":{
"result": "result.project funding level 0",
"project": "project.funding level 0",
"country":"country.organization.project.funding level 0",
"organization":"organization.project.funding level 0"
},
"co-funded":{
"result": "result.No of funders",
},
"foslvl1":
{
"publication":"publication.topics.result.result_fos.lvl1",
"result":"result.topics.result.result_fos.lvl1",
"indi_pub_downloads":"indi_pub_downloads.result.result_fos.lvl1",
"indi_pub_downloads_year":"indi_pub_downloads_year.result.result_fos.lvl1",
"indi_impact_measures":"indi_impact_measures.result.result_fos.lvl1",
"result_apc":"result_apc.result.result_fos.lvl1",
"result_apc_affiliations":"result_apc_affiliations.result.result_fos.lvl1",
"result_country":"result_country.result.result_fos.lvl1",
"historical_snapshots_irish_fos":"historical_snapshots_irish_fos.lvl1"
},
"foslvl2":{
"publication":"publication.topics.result.result_fos.lvl2",
"result":"result.topics.result.result_fos.lvl2",
"indi_pub_downloads":"indi_pub_downloads.result.result_fos.lvl2",
"indi_pub_downloads_year":"indi_pub_downloads_year.result.result_fos.lvl2",
"indi_impact_measures":"indi_impact_measures.result.result_fos.lvl2",
"result_apc":"result_apc.result.result_fos.lvl2",
"result_apc_affiliations":"result_apc_affiliations.result.result_fos.lvl2",
"result_country":"result_country.result.result_fos.lvl2",
"historical_snapshots_irish_fos":"historical_snapshots_irish_fos.lvl2"
},
"publicly-funded":{
"result":"result.indi_pub_publicly_funded.publicly_funded",
"indi_pub_downloads":"indi_pub_downloads.result.indi_pub_publicly_funded.publicly_funded",
"indi_pub_downloads_year":"indi_pub_downloads_year.result.indi_pub_publicly_funded.publicly_funded",
"indi_impact_measures":"indi_impact_measures.result.indi_pub_publicly_funded.publicly_funded",
"result_apc":"result_apc.result.indi_pub_publicly_funded.publicly_funded",
"result_apc_affiliations":"result_apc_affiliations.result.indi_pub_publicly_funded.publicly_funded",
"result_country":"result_country.result.indi_pub_publicly_funded.publicly_funded"
}
static getFilter(fieldPath: string, filterType:FilterType) {
if((filterType == "start_year" || filterType == "end_year") && (fieldPath.indexOf(".year") != -1 || fieldPath.indexOf(".start year") != -1)
&& fieldPath.indexOf("indi_pub_downloads_year.year") == -1){
// make it work for any table with field year or start year no matter the type or the table name
//exclude indi_pub_downloads_year.year because it throws errors. when there is a solution remove the exclusion
}
static getFieldForTable(field, table){
if(["publication", "software", "dataset", "other", "result"].indexOf(table)!=-1 && IndicatorFilterUtils.filteredFields[field]["result"]){
return IndicatorFilterUtils.filteredFields[field]["result"];
}else{
return IndicatorFilterUtils.filteredFields[field][table];
}
}
static getFilter(fieldPath: string, filterType:FilterType, value:string|string[] =null) {
let tablename = fieldPath.split(".")[0];
if ((filterType == "start_year" || filterType == "end_year") && (IndicatorFilterUtils.getFieldForTable("year",tablename))){
if (filterType == "start_year") {
return '{"groupFilters":[{"field":"' + fieldPath + '","type":">=","values":["' + ChartHelper.prefix + 'start_year' + ChartHelper.suffix + '"]}],"op":"AND"}';
return '{"groupFilters":[{"field":"' + IndicatorFilterUtils.getFieldForTable("year",tablename) + '","type":">=","values":["' + ChartHelper.prefix + 'start_year' + ChartHelper.suffix + '"]}],"op":"AND"}';
} else if (filterType == "end_year") {
return '{"groupFilters":[{"field":"' + fieldPath + '","type":"<=","values":["' + ChartHelper.prefix + 'end_year' + ChartHelper.suffix + '"]}],"op":"AND"}';
return '{"groupFilters":[{"field":"' + IndicatorFilterUtils.getFieldForTable("year",tablename) + '","type":"<=","values":["' + ChartHelper.prefix + 'end_year' + ChartHelper.suffix + '"]}],"op":"AND"}';
}
}
let table = fieldPath&&fieldPath.length > 0? fieldPath.split(".")[0]:"";
if(["publication", "software", "dataset", "other", "result"].indexOf(table)!=-1){
return this.getResultFilter(table,filterType);
}else if (table == "project"){
return this.getProjectFilter(filterType);
}
else if (table == "country"){
return this.getCountryFilter(filterType);
}else if (table == "organization"){
return this.getOrganizationFilter(filterType);
}
if (filterType == "fundingL0" && IndicatorFilterUtils.getFieldForTable("fundingL0",tablename)) {
return '{"groupFilters":[{"field":"' + IndicatorFilterUtils.getFieldForTable("fundingL0",tablename) + '","type":"=","values":["' + ChartHelper.prefix + 'fundingL0' + ChartHelper.suffix + '"]}],"op":"AND"}';
return "";
}
static getResultFilter(table: string = null, filterType:FilterType) {
//works for tables ["publication", "software", "dataset", "other", "result"]
if (filterType == "fundingL0") {
if(properties.useOldStatisticsSchema) {
return '{"groupFilters":[{"field":"' + table + '.project.funding level 0","type":"=","values":["' + ChartHelper.prefix + 'fundingL0' + ChartHelper.suffix + '"]}],"op":"AND"}';
}else{//new statistcs schema
return '{"groupFilters":[{"field":"' + table + '.project funding level 0","type":"=","values":["' + ChartHelper.prefix + 'fundingL0' + ChartHelper.suffix + '"]}],"op":"AND"}';
}else if (filterType == "co-funded" && IndicatorFilterUtils.getFieldForTable("co-funded",tablename)) {
return '{"groupFilters":[{"field":"' + IndicatorFilterUtils.getFieldForTable("co-funded",tablename) + '","type":">","values":["1"]}],"op":"AND"}';
}else if (filterType == "publicly-funded" && IndicatorFilterUtils.getFieldForTable("publicly-funded",tablename)) {
return '{"groupFilters":[{"field":"' + IndicatorFilterUtils.getFieldForTable("publicly-funded",tablename) + '","type":"=","values":["' + (value=="true"?"1":"0") +'"]}],"op":"AND"}';
}else if (filterType == "foslvl1" && IndicatorFilterUtils.getFieldForTable("foslvl1",tablename) && value && value.length > 0) {
let filterString = '{"groupFilters":[' ;
let filters = [];
for(let v of value) {
filters.push('{"field":"' + IndicatorFilterUtils.getFieldForTable("foslvl1",tablename) + '","type":"=","values":["' + v + '"]}');
}
}else if (filterType == "co-funded") {
return '{"groupFilters":[{"field":"' + table + '.No of funders","type":">","values":["1"]}],"op":"AND"}';
}
return "";
}
static getProjectFilter( filterType:FilterType) {
//works for table "project"
if (filterType == "fundingL0") {
return '{"groupFilters":[{"field":"project.funding level 0","type":"=","values":["' + ChartHelper.prefix + 'fundingL0' + ChartHelper.suffix + '"]}],"op":"AND"}';
}
return "";
}
static getOrganizationFilter( filterType:FilterType) {
//works for table "organization"
if (filterType == "fundingL0") {
return '{"groupFilters":[{"field":"organization.project.funding level 0","type":"=","values":["' + ChartHelper.prefix + 'fundingL0' + ChartHelper.suffix + '"]}],"op":"AND"}';
}
return "";
}
static getCountryFilter( filterType:FilterType) {
//works for table "country"
if (filterType == "fundingL0") {
return '{"groupFilters":[{"field":"country.organization.project.funding level 0","type":"=","values":["' + ChartHelper.prefix + 'fundingL0' + ChartHelper.suffix + '"]}],"op":"AND"}';
filterString+=filters.join(", ")
filterString+='],"op":"OR"}'
return filterString;
}else if (filterType == "foslvl2" && IndicatorFilterUtils.getFieldForTable("foslvl2",tablename) && value && value.length > 0) {
let filterString = '{"groupFilters":[' ;
let filters = [];
for(let v of value) {
filters.push('{"field":"' + IndicatorFilterUtils.getFieldForTable("foslvl2",tablename) + '","type":"=","values":["' + v + '"]}');
}
filterString+=filters.join(", ")
filterString+='],"op":"OR"}'
return filterString;
}
return "";
}
@ -297,12 +337,18 @@ export class IndicatorFilterUtils {
}
}
/**
* @deprecated
*
* TODO: Remove after merge with develop
* */
export enum StakeholderEntities {
STAKEHOLDER = 'Dashboard',
FUNDER = 'Funder',
RI = 'Research Initiative',
ORGANIZATION = 'Research Institution',
PROJECT = 'Project',
COUNTRY = 'National',
STAKEHOLDERS = 'Dashboards',
FUNDERS = 'Funders',
@ -310,10 +356,3 @@ export enum StakeholderEntities {
ORGANIZATIONS = 'Research Institutions',
PROJECTS = 'Projects'
}
export let stakeholderTypes: Option[] = [
{value: 'funder', label: StakeholderEntities.FUNDER},
{value: 'ri', label: StakeholderEntities.RI},
{value: 'organization', label: StakeholderEntities.ORGANIZATION},
{value: 'project', label: StakeholderEntities.PROJECT}
];

View File

@ -5,7 +5,7 @@ import {Meta, Title} from "@angular/platform-browser";
import {SEOService} from "../../sharedComponents/SEO/SEO.service";
import {Breadcrumb} from "../../utils/breadcrumbs/breadcrumbs.component";
import {Subscriber} from "rxjs";
import {StakeholderEntities} from "../entities/stakeholder";
import {StakeholderBaseComponent} from "../../monitor-admin/utils/stakeholder-base.component";
@Component({
selector: 'indicator-themes-page',
@ -30,10 +30,10 @@ import {StakeholderEntities} from "../entities/stakeholder";
This is the current set of indicator themes we cover. Well keep enriching it as new requests and data are coming into the <a href="https://graph.openaire.eu" class="text-graph" target="_blank">OpenAIRE Graph</a>. We are at your disposal, should you have any recommendations!
</p>
<p>
Check out the indicator pages (for <a [routerLink]="['../funder']" [relativeTo]="route">funders</a>,
<a [routerLink]="['../organization']" [relativeTo]="route">research institutions</a> and
<a [routerLink]="['../ri']" [relativeTo]="route">research initiatives</a>)
for the specific indicators for each type of dashboard, and the <a [routerLink]="['../../methodology']" [relativeTo]="route">methodology and terminology</a> page on how we produce the metrics.
Check out the indicator pages (for <a [routerLink]="['../funder']" [relativeTo]="_route">funders</a>,
<a [routerLink]="['../organization']" [relativeTo]="_route">research institutions</a> and
<a [routerLink]="['../ri']" [relativeTo]="_route">research initiatives</a>)
for the specific indicators for each type of dashboard, and the <a [routerLink]="['../../methodology']" [relativeTo]="_route">methodology and terminology</a> page on how we produce the metrics.
</p>
</div>
</div>
@ -54,10 +54,10 @@ import {StakeholderEntities} from "../entities/stakeholder";
This is the current set of indicator themes we cover. Well keep enriching it as new requests and data are coming into the <a href="https://graph.openaire.eu" class="text-graph" target="_blank">OpenAIRE Graph</a>. We are at your disposal, should you have any recommendations!
</p>
<p>
Check out the indicator pages (for <a [routerLink]="['../funder']" [relativeTo]="route" class="uk-text-lowercase">{{entities.FUNDERS}}</a>,
<a [routerLink]="['../organization']" [relativeTo]="route" class="uk-text-lowercase">{{entities.ORGANIZATIONS}}</a> and
<a [routerLink]="['../ri']" [relativeTo]="route" class="uk-text-lowercase">{{entities.RIS}}</a>)
for the specific indicators for each type of dashboard, and the <a [routerLink]="['../../methodology']" [relativeTo]="route">methodology and terminology</a> page on how we produce the metrics.
Check out the indicator pages (for <a [routerLink]="['../funder']" [relativeTo]="_route" class="uk-text-lowercase">{{entities.funders}}</a>,
<a [routerLink]="['../organization']" [relativeTo]="_route" class="uk-text-lowercase">{{entities.organizations}}</a> and
<a [routerLink]="['../ri']" [relativeTo]="_route" class="uk-text-lowercase">{{entities.ris}}</a>)
for the specific indicators for each type of dashboard, and the <a [routerLink]="['../../methodology']" [relativeTo]="_route">methodology and terminology</a> page on how we produce the metrics.
</p>
</div>
</div>
@ -65,44 +65,24 @@ import {StakeholderEntities} from "../entities/stakeholder";
</div>
`
})
export class IndicatorThemesComponent implements OnInit, OnDestroy {
private subscriptions: any[] = [];
public properties = properties;
public entities = StakeholderEntities;
export class IndicatorThemesComponent extends StakeholderBaseComponent implements OnInit {
public breadcrumbs: Breadcrumb[] = [{name: 'home', route: '/'}, {name: 'Resources - Themes'}];
constructor(private router: Router,
private meta: Meta,
private title: Title,
private seoService: SEOService,
public route: ActivatedRoute) {
constructor(protected _router: Router,
protected _meta: Meta,
protected _title: Title,
protected seoService: SEOService,
protected _route: ActivatedRoute) {
super();
}
ngOnInit() {
this.subscriptions.push(this.route.params.subscribe(params => {
const description = "Monitor | Indicator Themes";
const title = "Monitor | Indicator Themes";
this.metaTags(title, description);
this.subscriptions.push(this._route.params.subscribe(params => {
this.title = "Monitor | Indicator Themes";
this.description = "Monitor | Indicator Themes";
this.setMetadata();
this.breadcrumbs[0].route = '/' + (params['stakeholder']?params['stakeholder']:'');
this.breadcrumbs[0].name = (params['stakeholder']?'dashboard':'home');
}));
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => {
if (subscription instanceof Subscriber) {
subscription.unsubscribe();
}
});
}
metaTags(title, description) {
const url = properties.domain + properties.baseLink + this.router.url;
this.seoService.createLinkForCanonicalURL(url, false);
this.meta.updateTag({content: url}, "property='og:url'");
this.meta.updateTag({content: description}, "name='description'");
this.meta.updateTag({content: description}, "property='og:description'");
this.meta.updateTag({content: title}, "property='og:title'");
this.title.setTitle(title);
}
}

View File

@ -0,0 +1,484 @@
import {ChangeDetectorRef, Directive, HostListener, OnInit, ViewRef} from "@angular/core";
import {IndicatorStakeholderBaseComponent} from "../monitor-admin/utils/stakeholder-base.component";
import {DomSanitizer} from "@angular/platform-browser";
import {
Category,
Indicator, IndicatorPath,
IndicatorSize, Overlay,
Section,
Stakeholder,
SubCategory,
Topic,
Visibility
} from "./entities/stakeholder";
import {LayoutService} from "../dashboard/sharedComponents/sidebar/layout.service";
import {ClickEvent} from "../utils/click/click-outside-or-esc.directive";
import {Session, User} from "../login/utils/helper.class";
import {Filter, Value} from "../searchPages/searchUtils/searchHelperClasses.class";
import {RangeFilter} from "../utils/rangeFilter/rangeFilterHelperClasses.class";
import {RangeFilterComponent} from "../utils/rangeFilter/rangeFilter.component";
import {Dates, StringUtils} from "../utils/string-utils.class";
import {Params} from "@angular/router";
import {StatisticsService} from "../monitor-admin/utils/services/statistics.service";
import {SearchResearchResultsService} from "../services/searchResearchResults.service";
import {CustomFilterService} from "../shared/customFilter.service";
@Directive()
export abstract class MonitorIndicatorStakeholderBaseComponent extends IndicatorStakeholderBaseComponent implements OnInit {
/** Status */
public loading: boolean = true;
public isMobile: boolean = false;
public isFullscreen: boolean = false;
/** Variables */
public user: User;
public requireLogin: boolean = true;
public view: Visibility;
public stakeholder: Stakeholder;
public activeTopic: Topic = null;
public activeCategory: Category = null;
public activeSubCategory: SubCategory = null;
public filters: Filter[] = [];
public queryParams: any = {};
public periodFilter: RangeFilter = {
title: "Time range",
filterId: "year",
originalFilterIdFrom: null,
originalFilterIdTo: null,
selectedFromValue: null,
selectedToValue: null,
selectedFromAndToValues: ""
};
rangeFilter: RangeFilterComponent;
public numberResults: Map<string, number> = new Map<string, number>();
public chartsActiveType: Map<string, IndicatorPath> = new Map<string, IndicatorPath>();
public currentYear = new Date().getFullYear();
public clipboard;
/** Services */
protected sanitizer: DomSanitizer;
protected cdr: ChangeDetectorRef;
protected layoutService: LayoutService;
protected statisticsService: StatisticsService;
protected searchResearchResultsService: SearchResearchResultsService;
protected customFilterService: CustomFilterService;
@HostListener('fullscreenchange', ['$event'])
@HostListener('webkitfullscreenchange', ['$event'])
@HostListener('mozfullscreenchange', ['$event'])
@HostListener('MSFullscreenChange', ['$event'])
screenChange() {
this.isFullscreen = !this.isFullscreen;
}
ngOnInit() {
this.layoutService.isMobile.subscribe(isMobile => {
this.isMobile = isMobile;
this.cdr.detectChanges();
});
this.createClipboard();
}
protected setView(params: Params) {
this.setSelectedFilters();
this.loading = false;
if (params['topic']) {
this.activeTopic = this.stakeholder.topics.find(topic => topic.alias === decodeURIComponent(params['topic']) && this.hasPermission(topic.visibility));
if (this.activeTopic) {
if (params['category']) {
this.activeCategory = this.activeTopic.categories.find(category =>
(category.alias === params['category']) && this.hasPermission(category.visibility));
if (!this.activeCategory) {
this.navigateToError();
return;
}
} else {
this.activeCategory = this.activeTopic.categories.find(category => this.hasPermission(category.visibility));
if (this.activeCategory) {
this.activeSubCategory = this.activeCategory.subCategories.find(subCategory =>
this.hasPermission(subCategory.visibility));
if (this.activeSubCategory) {
this.setIndicators();
}
}
return;
}
if (this.activeCategory) {
if (params['subCategory']) {
this.activeSubCategory = this.activeCategory.subCategories.find(subCategory =>
(subCategory.alias === params['subCategory'] && this.hasPermission(subCategory.visibility)));
if (!this.activeSubCategory) {
this.navigateToError();
return;
}
} else {
this.activeSubCategory = this.activeCategory.subCategories.find(subCategory =>
this.hasPermission(subCategory.visibility));
}
if (this.activeSubCategory) {
this.setIndicators();
} else {
this.navigateToError();
}
return;
} else {
this.activeSubCategory = null;
}
} else {
this.navigateToError();
return;
}
} else {
this.activeTopic = this.stakeholder.topics.find(topic => this.hasPermission(topic.visibility));
if (this.activeTopic) {
this.activeCategory = this.activeTopic.categories.find(category => this.hasPermission(category.visibility));
if (this.activeCategory) {
this.activeSubCategory = this.activeCategory.subCategories.find(subCategory => this.hasPermission(subCategory.visibility));
if (this.activeSubCategory) {
this.setIndicators();
}
}
}
}
}
protected handleQueryParams(queryParams, params) {
this.queryParams = Object.assign({}, queryParams);
this.initializeFilters();
this.setView(params);
if(this.requireLogin && !this.user && (this.filters.filter(filter => this.queryParams[filter.filterId]).length > 0 || this.queryParams['year'])) {
if(queryParams['view']) {
this._router.navigate([], {queryParams: {view: queryParams['view']}});
} else {
this._router.navigate([], {queryParams: {}});
}
}
this.view = queryParams['view'];
}
protected initializeFilters() {
this.periodFilter.selectedFromValue = (this.queryParams['year'] && this.queryParams['year'].indexOf("range") == 0) ? this.queryParams['year'].split("range")[1].split(":")[0] : "";
this.periodFilter.selectedToValue = (this.queryParams['year'] && this.queryParams['year'].indexOf("range") == 0) ? this.queryParams['year'].split("range")[1].split(":")[1] : "";
this.validateYearRange(false);
for (let filter of this.filters) {
if (this.queryParams[filter.filterId]) {
for (let value of filter.values) {
if (value.id == StringUtils.URIDecode(StringUtils.unquote(this.queryParams[filter.filterId]))) {
value.selected = true;
filter.countSelectedValues = 1;
break;
}
}
} else {
this.clearFilter(filter);
}
}
}
protected validateYearRange(navigateTo: boolean = false) {
let validYears = true;
if (this.periodFilter.selectedToValue && (this.periodFilter.selectedToValue.length == 0 || !Dates.isValidYear(this.periodFilter.selectedToValue, Dates.currentYear - 20, Dates.currentYear))) {
this.periodFilter.selectedToValue = Dates.currentYear + "";
validYears = false;
}
if (this.periodFilter.selectedFromValue && (this.periodFilter.selectedFromValue.length == 0 || !Dates.isValidYear(this.periodFilter.selectedFromValue, Dates.currentYear - 20, Dates.currentYear))) {
this.periodFilter.selectedFromValue = Dates.currentYear - 20 + "";
validYears = false;
}
if (this.periodFilter.selectedFromValue && this.periodFilter.selectedFromValue.length && this.periodFilter.selectedToValue && this.periodFilter.selectedToValue.length > 0 && parseInt(this.periodFilter.selectedFromValue, 10) > parseInt(this.periodFilter.selectedToValue, 10)) {
this.periodFilter.selectedFromValue = this.periodFilter.selectedToValue;
validYears = false;
}
if (!validYears || navigateTo) {
if (this.periodFilter.selectedFromValue || this.periodFilter.selectedToValue) {
this.queryParams["year"] = 'range' + (this.periodFilter.selectedFromValue ? this.periodFilter.selectedFromValue : '') + ":" + (this.periodFilter.selectedToValue ? this.periodFilter.selectedToValue : "");
} else {
delete this.queryParams["year"];
}
this._router.navigate([], {queryParams: this.queryParams});
this.setIndicators();
}
}
protected getFullUrl(indicatorPath: IndicatorPath) {
let fosValues = this.getSelectedFilterValues("fos");
return this.indicatorUtils.getFullUrlWithFilters(this.stakeholder, indicatorPath,null, this.periodFilter.selectedFromValue, this.periodFilter.selectedToValue, false, fosValues?fosValues.lvl1:[],fosValues?fosValues.lvl2:[], this.getSelectedFilterValues("publiclyfunded"));
}
protected setIndicators() {
this.periodFilter.selectedFromAndToValues = (this.periodFilter.selectedFromValue || this.periodFilter.selectedToValue ? ((this.periodFilter.selectedFromValue && !this.periodFilter.selectedToValue ? "From " : "") + (!this.periodFilter.selectedFromValue && this.periodFilter.selectedToValue ? "Until " : "") + (this.periodFilter.selectedFromValue ? this.periodFilter.selectedFromValue : "") +
(this.periodFilter.selectedFromValue && this.periodFilter.selectedToValue ? " - " : "") + (this.periodFilter.selectedToValue ? this.periodFilter.selectedToValue : "")) : "");
//clear numbers when filters change
this.numberResults.clear();
let urls: Map<string, [number, number][]> = new Map<string, [number, number][]>();
this.activeSubCategory.numbers.forEach((section, i) => {
section.indicators.forEach((number, j) => {
if (this.hasPermission(number.visibility)) {
let url = this.getFullUrl(number.indicatorPaths[0]);
const pair = JSON.stringify([number.indicatorPaths[0].source, url]);
const indexes = urls.get(pair) ? urls.get(pair) : [];
indexes.push([i, j]);
urls.set(pair, indexes);
}
});
});
urls.forEach((indexes, pair) => {
pair = JSON.parse(pair);
let activeSubcategory = this.activeSubCategory._id;
this.subscriptions.push(this.statisticsService.getNumbers(this.indicatorUtils.getSourceType(pair[0]), pair[1]).subscribe(response => {
if(activeSubcategory === this.activeSubCategory._id) {
indexes.forEach(([i, j]) => {
if( this.activeSubCategory?.numbers[i]?.indicators[j]) {
let result = JSON.parse(JSON.stringify(response));
this.activeSubCategory.numbers[i].indicators[j].indicatorPaths[0].jsonPath.forEach(jsonPath => {
if (result) {
result = result[jsonPath];
}
});
if (typeof result === 'string' || typeof result === 'number') {
result = Number(result);
if (result === Number.NaN) {
result = 0;
}
} else {
result = 0;
}
this.numberResults.set(i + '-' + j, result);
}
});
}
}));
});
this.activeSubCategory.charts.forEach((section, i) => {
section.indicators.forEach((indicator, j) => {
if (indicator.indicatorPaths.length > 0) {
indicator.indicatorPaths[0].safeResourceUrl = this.getUrlByStakeHolder(indicator.indicatorPaths[0]);
this.chartsActiveType.set(i + '-' + j, indicator.indicatorPaths[0]);
}
});
});
if (this.cdr && !(this.cdr as ViewRef).destroyed) {
this.cdr.detectChanges();
}
}
public getUrlByStakeHolder(indicatorPath: IndicatorPath) {
return this.sanitizer.bypassSecurityTrustResourceUrl(
this.indicatorUtils.getChartUrl(indicatorPath.source, this.getFullUrl(indicatorPath)));
}
public setActiveChart(i: number, j: number, type: string) {
let activeChart = this.activeSubCategory.charts[i].indicators[j].indicatorPaths.filter(indicatorPath => indicatorPath.type === type)[0];
activeChart.safeResourceUrl = this.getUrlByStakeHolder(activeChart);
this.chartsActiveType.set(i + '-' + j, activeChart);
}
public filter() {
this.validateYearRange(true);
}
public filterChanged($event, navigate: boolean = true) {
let selected = [];
for (let value of $event.value.values) {
if (value.selected) {
selected.push( StringUtils.quote(StringUtils.URIEncode(value.id)));
}
}
if (selected.length > 0) {
this.queryParams[$event.value.filterId] = selected.join(",");
} else {
delete this.queryParams[$event.value.filterId];
}
if (navigate) {
this._router.navigate([], {queryParams: this.queryParams});
this.setIndicators();
}
}
public countSelectedFilters(): number {
let count = 0;
if (this.periodFilter.selectedFromAndToValues.length > 0) {
count += 1;
}
for (let filter of this.filters) {
count += filter.countSelectedValues;
}
return count;
}
public clearAll() {
for (let filter of this.filters) {
this.clearFilter(filter);
}
this.periodFilter.selectedFromValue = "";
this.periodFilter.selectedToValue = "";
this.validateYearRange(true)
}
public clearFilter(filter: Filter) {
filter.countSelectedValues = 0;
filter.radioValue = "";
for (let value of filter.values) {
if (value.selected) {
value.selected = false;
}
}
if (this.queryParams[filter.filterId]) {
delete this.queryParams[filter.filterId];
}
}
clearPeriodFilter() {
if (this.periodFilter.selectedFromValue || this.periodFilter.selectedToValue) {
this.periodFilter.selectedFromValue = "";
this.periodFilter.selectedToValue = "";
if(this.rangeFilter) {
this.rangeFilter.clearFilter();
}
this.filter();
}
}
clearFilterValue(filter: Filter, value: Value) {
value.selected = false;
filter.radioValue = '';
filter.countSelectedValues = filter.countSelectedValues - 1;
this.filterChanged({
value:filter
});
}
public hasPermission(visibility: Visibility): boolean {
if(visibility === 'PUBLIC') {
return true;
} else if(visibility === 'RESTRICTED') {
return (!this.view || this.view === 'RESTRICTED') && this.isMember(this.stakeholder);
} else {
return !this.view && this.isManager(this.stakeholder);
}
}
public isMember(stakeholder: Stakeholder) {
return this.user && (Session.isPortalAdministrator(this.user) || Session.isCurator(stakeholder.type, this.user)
|| Session.isManager(stakeholder.type, stakeholder.alias, this.user) || Session.isMember(stakeholder.type, stakeholder.alias, this.user));
}
public isManager(stakeholder: Stakeholder) {
return this.user && (Session.isPortalAdministrator(this.user) || Session.isCurator(stakeholder.type, this.user) || Session.isManager(stakeholder.type, stakeholder.alias, this.user));
}
public countSubCategoriesToShow(category: Category): number {
return category.subCategories.filter(subCategory => this.hasPermission(subCategory.visibility)).length;
}
public countSectionsWithIndicatorsToShow(sections: Section[]):number {
return sections.map(section => this.countIndicatorsToShow(section.indicators)).reduce((sum, current) => sum + current, 0);
}
public countIndicatorsToShow(indicators: Indicator[]): number {
return indicators.filter(indicator => this.hasPermission(indicator.visibility)).length;
}
public getNumberClassBySize(size: IndicatorSize) {
if (size === 'small') {
return 'uk-width-medium@m uk-width-1-1';
} else if (size === 'medium') {
return 'uk-width-1-4@l uk-width-1-2@m uk-width-1-1';
} else {
return 'uk-width-1-2@l uk-width-1-1@m uk-width-1-1';
}
}
public getChartClassBySize(size: IndicatorSize): string {
if (size === 'small') {
return 'uk-width-1-3@xl uk-width-1-2@m uk-width-1-1';
} else if (size === 'medium') {
return 'uk-width-1-2@l uk-width-1-1';
} else {
return 'uk-width-1-1';
}
}
public printReport() {
window.print();
}
changeOverlay(event, indicator: Indicator, overlay: Overlay) {
event.stopPropagation();
indicator.overlay = overlay;
}
closeOverlay(event: ClickEvent, indicator: Indicator) {
if(event.clicked && indicator.overlay) {
indicator.overlay = false;
}
}
private createClipboard() {
if (typeof window !== 'undefined') {
delete this.clipboard;
let Clipboard;
Clipboard = require('clipboard');
this.clipboard = new Clipboard('.clipboard_btn');
}
}
//Refine Type Filters
setSelectedFilters(){
for (var i = 0; i < this.filters.length; i++) {
var filter: Filter = this.filters[i];
filter.countSelectedValues = 0;
let parameterNames = Object.keys(this.queryParams);
if (parameterNames.indexOf(filter.filterId) != -1) {
let values = (decodeURIComponent(this.queryParams[filter.filterId])).split(/,(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/, -1);
for (let filterValue of filter.values) {
if (values.indexOf(StringUtils.quote(filterValue.id)) > -1) {
filterValue.selected = true;
filter.countSelectedValues++;
} else {
filterValue.selected = false;
}
}
} else {
for (let filterValue of filter.values) {
filterValue.selected = false;
}
}
}
}
getSelectedFilterValues(filterId){
let values = null;
for (let filter of this.filters) {
if(filterId == filter.filterId && filter.countSelectedValues > 0) {
values =filterId == "fos"?{lvl1:[],lvl2:[]}:[];
for (let filterValue of filter.values) {
if (filterValue.selected) {
if(filterId == "fos"){
let code = filterValue.id.split(" ")[0];
if(code.length == 2){
values.lvl1.push(filterValue.id)
}else{
values.lvl2.push(filterValue.id)
}
}else if(filterId == "publiclyfunded"){
// console.log("publiclyFunded", filterValue)
return filterValue.id;
}else{
values.push(filterValue.id);
}
}
}
}
}
return values;
}
}

View File

@ -1,12 +1,12 @@
import {Injectable} from "@angular/core";
import {MenuItem} from "../../sharedComponents/menu";
import {Option} from "../../sharedComponents/input/input.component";
import {StakeholderEntities} from "../entities/stakeholder";
import {from, Subscription} from "rxjs";
import {HttpClient} from "@angular/common/http";
import {properties} from "../../../../environments/environment";
import {Page} from "../../utils/entities/adminTool/page";
import {map} from "rxjs/operators";
import {StakeholderConfiguration} from "../../monitor-admin/utils/indicator-utils";
@Injectable({
providedIn: 'root'
@ -16,12 +16,7 @@ export class ResourcesService {
private subscription: Subscription;
private routes = ResourcesService.types.map(type => '/indicators/' + type.value);
public static readonly types: Option[] = [
{value: 'funder', label: StakeholderEntities.FUNDERS},
{value: 'ri', label: StakeholderEntities.RIS},
{value: 'project', label: StakeholderEntities.PROJECTS},
{value: 'organization', label: StakeholderEntities.ORGANIZATIONS}
];
public static readonly types: Option[] = StakeholderConfiguration.TYPES;
constructor(private http: HttpClient) {
}

View File

@ -53,6 +53,30 @@ export class StakeholderService {
}
return from(this.getStakeholderAsync());
}
getResearcherStakeholder( orcid, name, results, shouldUpdate: boolean = false): Observable<Stakeholder> {
if (!this.stakeholderSubject.value || this.stakeholderSubject.value.alias !== orcid || shouldUpdate) {
this.promise = new Promise<void>((resolve, reject) => {
this.sub = this.http.get<Stakeholder>(properties.monitorServiceAPIURL + '/stakeholder/' + encodeURIComponent("researcher"), CustomOptions.registryOptions()).pipe(map(stakeholder => {
return this.formalize(this.checkIsUpload(stakeholder));
})).subscribe(stakeholder => {
stakeholder.index_id = orcid;
stakeholder.index_name = name;
stakeholder.name = name;
stakeholder.alias = orcid;
if(results <7 && stakeholder.topics[0]?.categories[0]?.subCategories[0]){
stakeholder.topics[0].categories[0].subCategories[0].charts=[]; // keep only numbers - charts wont show much anyway
}
this.stakeholderSubject.next(stakeholder);
resolve();
}, error => {
let stakeholder = new Stakeholder(null,"researcher", orcid,name,name,orcid,"PUBLIC", null, null,"");
this.stakeholderSubject.next(stakeholder);
resolve();
});
});
}
return from(this.getStakeholderAsync());
}
async getStakeholderAsync() {
if (this.promise) {

View File

@ -1,4 +1,5 @@
@import "~src/assets/openaire-theme/less/_import-variables";
@import (reference) "~src/assets/openaire-theme/less/_import-variables";
@import (optional) "~src/assets/extend-theme/less/_import-variables";
#notifications-switcher {
top: 480px !important;

View File

@ -40,7 +40,7 @@ export class NotificationConfiguration {
<span *ngIf="mobile" class="uk-margin-right">
<icon ratio="1.5" name="west" visuallyHidden="back" [flex]="true"></icon>
</span>
<div class="uk-text-bold">Notifications</div>
<span class="uk-text-bold">Notifications</span>
</h4>
<div class="uk-flex uk-flex-right@m uk-flex-center uk-padding uk-padding-remove-vertical">
<button [disabled]="unreadCount === 0" (click)="readAll()" class="uk-button uk-button-link">Mark As Read ({{unreadCount}})</button>
@ -75,7 +75,7 @@ export class NotificationConfiguration {
<span class="uk-margin-right">
<icon ratio="1.5" name="west" visuallyHidden="back" [flex]="true"></icon>
</span>
<div *ngIf="notification.title" class="uk-text-bold">{{notification.title}}</div>
<span *ngIf="notification.title" class="uk-text-bold">{{notification.title}}</span>
</h4>
<div class="uk-flex uk-flex-middle uk-margin-medium-bottom">
<notification-user [name]="notification.name" [surname]="notification.surname" colorClass="uk-text-secondary" [outline]="true"></notification-user>

View File

@ -7,11 +7,12 @@ import {NotificationService} from "../notification.service";
import {Notification} from "../notifications";
import {InputComponent, Option} from "../../sharedComponents/input/input.component";
import {NotificationHandler} from "../../utils/notification-handler";
import {BaseComponent} from "../../sharedComponents/base/base.component";
@Component({
selector: '[notify-form]',
template: `
<form *ngIf="user && form" [formGroup]="form">
<form *ngIf="user && form && properties.notificationsAPIURL" [formGroup]="form">
<ng-template [ngIf]="form.get('notify')">
<label><input name="notify" type="checkbox" class="uk-checkbox" formControlName="notify"><span class="uk-margin-small-left">{{label}}</span></label>
<div [class.uk-hidden]="!form.get('notify').value" class="uk-position-relative uk-margin-medium-top">
@ -44,7 +45,7 @@ import {NotificationHandler} from "../../utils/notification-handler";
styleUrls: ['notify-form.component.less'],
encapsulation: ViewEncapsulation.None
})
export class NotifyFormComponent implements OnInit, OnDestroy {
export class NotifyFormComponent extends BaseComponent implements OnInit {
@Input()
public label: string = 'Notify Managers';
public form: UntypedFormGroup;
@ -57,13 +58,13 @@ export class NotifyFormComponent implements OnInit, OnDestroy {
public focused: boolean = false;
@ViewChild('recipients', { static: false }) recipients: InputComponent;
private notification: Notification;
private subscriptions: any[] = [];
public sending: boolean = false;
constructor(private fb: UntypedFormBuilder,
private cdr: ChangeDetectorRef,
private userManagementService: UserManagementService,
private notificationService: NotificationService) {
super();
}
@Input()
@ -79,14 +80,6 @@ export class NotifyFormComponent implements OnInit, OnDestroy {
}));
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => {
if (subscription instanceof Subscription) {
subscription.unsubscribe();
}
})
}
reset(message: string = null) {
if (!this.availableGroups) {
this.form = this.fb.group({
@ -116,7 +109,7 @@ export class NotifyFormComponent implements OnInit, OnDestroy {
}
sendNotification(notification: Notification = null) {
if (this.message) {
if (this.message && this.properties.notificationsAPIURL) {
if(notification === null) {
notification = new Notification('CUSTOM', [this.service], null, null);
notification.groups = this.parseGroups();
@ -162,16 +155,12 @@ export class NotifyFormComponent implements OnInit, OnDestroy {
}
get message(): string {
if ((this.form.get('notify') && !this.form.get('notify').value) || (this.groupsAsFromArray && this.groupsAsFromArray.length === 0)) {
if (!this.properties.notificationsAPIURL || (this.form.get('notify') && !this.form.get('notify').value) || (this.groupsAsFromArray && this.groupsAsFromArray.length === 0)) {
return null;
}
return this.form.get('message').value;
}
onFocus(event: boolean) {
this.focused = event;
}
focus(event) {
this.focused = true;
event.stopPropagation();

View File

@ -32,10 +32,14 @@ declare var UIkit: any;
My ORCID links
</div>
<span *ngIf="authorNameParam" class="uk-width-1-1 uk-width-expand@m">
<a class="uk-button-text uk-align-left uk-align-right@m" [queryParams]="authorNameParam"
<a *ngIf="!properties.orcidDiscoverLinksPage" class="uk-button uk-button-text uk-align-left uk-align-right@m" [queryParams]="authorNameParam"
[routerLink]="properties.searchLinkToAdvancedResults" routerLinkActive="router-link-active">
Discover {{openaireEntities.RESULTS | lowercase}} related to you
</a>
<a *ngIf="properties.orcidDiscoverLinksPage" class="uk-button uk-button-text uk-align-left uk-align-right@m"
[routerLink]="properties.orcidDiscoverLinksPage" routerLinkActive="router-link-active">
Discover {{openaireEntities.RESULTS | lowercase}} related to you
</a>
</span>
</div>
@ -203,7 +207,7 @@ export class MyOrcidLinksComponent {
this.subscriptions.push(this._orcidService.getPersonalDetails().subscribe(
details => {
let author: string = "";
console.log(details)
if(details && details['name']) {
let name: string = details['name'];
if(name['given-names'] && name['given-names']['value']) {

View File

@ -12,6 +12,8 @@ import {EnvProperties} from "../utils/properties/env-properties";
import {UserManagementService} from "../services/user-management.service";
import {OpenaireEntities} from "../utils/properties/searchFields";
import {FullScreenModalComponent} from "../utils/modal/full-screen-modal/full-screen-modal.component";
import {LogService} from "../utils/log/log.service";
import {UserProfileService} from "../services/userProfile.service";
declare var UIkit: any;
@ -20,88 +22,54 @@ declare var UIkit: any;
template: `
<ng-container *ngIf="pageType == 'landing' || pageType == 'search'">
<span *ngIf="!putCodes || putCodes.length == 0"
(click)="currentAction='add'; saveWorkPreparation();"
[class.uk-disabled]="showLoading || !isLoggedIn || (!pids && (!identifiers || identifiers.size == 0))"
[class.clickable]="!showLoading && isLoggedIn && (pids || (identifiers && identifiers.size > 0))"
[ngClass]="isMobile && pageType == 'landing' ? ' uk-width-1-1' : ''"
[attr.uk-tooltip]="isMobile? 'cls: uk-invisible' : 'pos: bottom; cls: uk-active uk-text-small uk-padding-small'"
[title]="((!pids && (!identifiers || identifiers.size == 0)) || !isLoggedIn) ? ((!pids && (!identifiers || identifiers.size == 0)) ? tooltipNoPid : tooltipNoLoggedInUser) : tooltipAdd">
<a class="uk-flex uk-flex-middle uk-button-link"
[ngClass]="isMobile && !(pageType == 'landing') ? 'uk-margin-left' : ''"
[class.uk-text-bolder]="!(isMobile && pageType == 'landing')"
[class.uk-text-muted]="showLoading || !isLoggedIn || (!pids && (!identifiers || identifiers.size == 0))"
(mouseover)="hoverEvent($event)" (mouseout)="hoverEvent($event)">
<icon *ngIf="!showLoading" [class.text-orcid]="!showLoading && isLoggedIn && (pids || identifiers?.size > 0)"
[class.uk-text-muted]="showLoading || !isLoggedIn || (!pids && (!identifiers || identifiers.size == 0))"
name="orcid_add" [ratio]="(isMobile && pageType == 'search') ? 0.7 : 1" visuallyHidden="add"></icon>
<span *ngIf="showLoading" class="uk-icon"><loading
[top_margin]="false" [size]="'small'"></loading></span>
<span [ngClass]="(isMobile && pageType == 'landing') ? 'uk-margin-small-left' : 'uk-margin-xsmall-left'">Claim</span>
</a>
<div *ngIf="isMobile && pageType == 'landing'" class="uk-margin-xsmall-top uk-padding uk-padding-remove-vertical uk-text-meta uk-text-xsmall"
[innerHTML]="((!pids && (!identifiers || identifiers.size == 0)) || !isLoggedIn) ? ((!pids && (!identifiers || identifiers.size == 0)) ? tooltipNoPid : tooltipNoLoggedInUser) : tooltipAdd"></div>
[ngClass]="isMobile && pageType == 'landing' ? ' uk-width-1-1' : ''"
[attr.uk-tooltip]="isMobile? 'cls: uk-invisible' : 'pos: bottom; cls: uk-active uk-text-small uk-padding-small'"
[title]="(noPids || !isLoggedIn) ? ((noPids) ? tooltipNoPid : tooltipNoLoggedInUser) : tooltipAdd">
<span (click)="currentAction='add'; saveWorkPreparation();"
[class.uk-disabled]="isDisabled"
[class.clickable]="!isDisabled">
<a class="uk-flex uk-flex-middle uk-button-link"
[ngClass]="isMobile && !(pageType == 'landing') ? 'uk-margin-left' : ''"
[class.uk-text-bolder]="!(isMobile && pageType == 'landing')"
[class.uk-text-muted]="isDisabled">
<icon *ngIf="!showLoading" [class.text-orcid]="properties.environment != 'beta' && !showLoading && isLoggedIn && (pids || identifiers?.size > 0)"
[class.uk-text-muted]="isDisabled"
name="orcid_add" [ratio]="(isMobile && pageType == 'search') ? 0.7 : 1" visuallyHidden="add"></icon>
<span *ngIf="showLoading" class="uk-icon"><loading
[top_margin]="false" [size]="'small'"></loading></span>
<span [ngClass]="(isMobile && pageType == 'landing') ? 'uk-margin-small-left' : 'uk-margin-xsmall-left'">Claim</span>
</a>
<div *ngIf="isMobile && pageType == 'landing'" class="uk-margin-xsmall-top uk-padding uk-padding-remove-vertical uk-text-meta uk-text-xsmall"
[innerHTML]="(noPids || !isLoggedIn) ? ((noPids) ? tooltipNoPid : tooltipNoLoggedInUser) : tooltipAdd"></div>
</span>
</span>
<span *ngIf="putCodes && putCodes.length > 0"
(click)="currentAction='delete'; deleteWorks();"
[class.uk-disabled]="showLoading || !isLoggedIn || (!pids && (!identifiers || identifiers.size == 0))"
[class.clickable]="!showLoading && isLoggedIn && (pids || (identifiers && identifiers.size > 0))"
[ngClass]="isMobile && pageType == 'landing' ? ' uk-width-1-1' : ''"
<span *ngIf="putCodes && putCodes.length > 0" [ngClass]="isMobile && pageType == 'landing' ? ' uk-width-1-1' : ''"
[attr.uk-tooltip]="isMobile? 'cls: uk-invisible' : 'pos: bottom; cls: uk-active uk-text-small uk-padding-small'"
[title]="((!pids && (!identifiers || identifiers.size == 0)) || !isLoggedIn) ? ((!pids && (!identifiers || identifiers.size == 0)) ? tooltipNoPid : tooltipNoLoggedInUser) : tooltipDelete">
<a class="uk-flex uk-flex-middle uk-button-link"
[ngClass]="isMobile && !(pageType == 'landing') ? 'uk-margin-left' : ''"
[class.uk-text-bolder]="!(isMobile && pageType == 'landing')"
[class.uk-text-muted]="showLoading || !isLoggedIn || (!pids && (!identifiers || identifiers.size == 0))"
(mouseover)="hoverEvent($event, 'delete')" (mouseout)="hoverEvent($event, 'delete')">
<icon *ngIf="!showLoading" [class.text-orcid]="!showLoading && isLoggedIn && (pids || identifiers?.size > 0)"
[class.uk-text-muted]="showLoading || !isLoggedIn || (!pids && (!identifiers || identifiers.size == 0))"
name="orcid_bin" [ratio]="(isMobile && pageType == 'search') ? 0.7 : 1" visuallyHidden="delete"></icon>
<span *ngIf="showLoading" class="uk-icon"><loading
[top_margin]="false" [size]="'small'"></loading></span>
<span [ngClass]="(isMobile && pageType == 'landing') ? 'uk-margin-small-left' : 'uk-margin-xsmall-left'">Remove</span>
</a>
<div *ngIf="isMobile && pageType == 'landing'" class="uk-margin-xsmall-top uk-padding uk-padding-remove-vertical uk-text-meta uk-text-xsmall"
[innerHTML]="((!pids && (!identifiers || identifiers.size == 0)) || !isLoggedIn) ? ((!pids && (!identifiers || identifiers.size == 0)) ? tooltipNoPid : tooltipNoLoggedInUser) : tooltipDelete"></div>
[title]="(noPids || !isLoggedIn) ? ((noPids) ? tooltipNoPid : tooltipNoLoggedInUser) : tooltipDelete">
<span (click)="currentAction='delete'; deleteWorks();"
[class.uk-disabled]="isDisabled"
[class.clickable]="!isDisabled">
<a class="uk-flex uk-flex-middle uk-button-link"
[ngClass]="isMobile && !(pageType == 'landing') ? 'uk-margin-left' : ''"
[class.uk-text-bolder]="!(isMobile && pageType == 'landing')"
[class.uk-text-muted]="isDisabled">
<icon *ngIf="!showLoading" [class.text-orcid]="!showLoading && isLoggedIn && (pids || identifiers?.size > 0)"
[class.uk-text-muted]="isDisabled"
name="orcid_bin" [ratio]="(isMobile && pageType == 'search') ? 0.7 : 1" visuallyHidden="delete"></icon>
<span *ngIf="showLoading" class="uk-icon"><loading
[top_margin]="false" [size]="'small'"></loading></span>
<span [ngClass]="(isMobile && pageType == 'landing') ? 'uk-margin-small-left' : 'uk-margin-xsmall-left'">Remove</span>
</a>
<div *ngIf="isMobile && pageType == 'landing'" class="uk-margin-xsmall-top uk-padding uk-padding-remove-vertical uk-text-meta uk-text-xsmall"
[innerHTML]="(noPids || !isLoggedIn) ? ((noPids) ? tooltipNoPid : tooltipNoLoggedInUser) : tooltipDelete"></div>
</span>
</span>
<!-- Old 'remove' code -->
<!-- <span *ngIf="putCodes && putCodes.length > 0"
[attr.uk-tooltip]="'pos: bottom; cls: uk-active uk-text-small uk-padding-small'"
[title]="(!pids || !isLoggedIn) ? (!pids ? tooltipNoPid : tooltipNoLoggedInUser) : tooltipDelete">
<a *ngIf="!showLoading" (click)="currentAction='delete'; deleteWorks();"
class="uk-icon-button uk-icon landing-action-button landing-action-button-orcid"
[class.uk-disabled]="showLoading || !isLoggedIn || !pids"
(mouseover)="hoverEvent($event, 'delete')" (mouseout)="hoverEvent($event, 'delete')">
<icon *ngIf="!hoverDelete" name="orcid_bin" ratio="1.1" visuallyHidden="delete"></icon>
<icon *ngIf="hoverDelete" name="delete_outline" class="uk-text-danger" visuallyHidden="delete"></icon>
</a>
<span *ngIf="showLoading" class="uk-icon icon-button uk-icon-button-small"><loading
[top_margin]="false" [size]="'small'"></loading></span>
</span> -->
</ng-container>
<modal-alert *ngIf="!isMobile" #grantModal [overflowBody]=false (alertOutput)="openGrantWindow()">
<div>
<div>{{requestGrantMessage}}</div>
<!-- <div class="uk-margin-medium-top uk-align-right">-->
<!-- <button (click)="closeGrantModal()" type="submit"-->
<!-- class="uk-button uk-padding-small uk-padding-remove-vertical uk-button-default">-->
<!-- <span>Cancel</span>-->
<!-- </button>-->
<!-- <button (click)="openGrantWindow()" type="submit"-->
<!-- class="uk-button uk-padding-small uk-padding-remove-vertical uk-margin-left uk-button-primary">-->
<!-- <span>Grant OpenAIRE</span>-->
<!-- </button>-->
<!--&lt;!&ndash; <button (click)="openGrantWindow()" type="submit"&ndash;&gt;-->
<!--&lt;!&ndash; class="uk-button uk-padding-small uk-padding-remove-vertical uk-margin-left orcid-button">&ndash;&gt;-->
<!--&lt;!&ndash; <img src="assets/common-assets/common/ORCIDiD_icon16x16.png" alt="">{{" "}}&ndash;&gt;-->
<!--&lt;!&ndash; <span>Create or Connect your ORCID iD</span>&ndash;&gt;-->
<!--&lt;!&ndash; </button>&ndash;&gt;-->
<!-- </div>-->
</div>
</modal-alert>
@ -290,18 +258,6 @@ declare var UIkit: any;
instead.
</div>
</div>
<!-- <div class="uk-margin-medium-top uk-align-right">-->
<!-- <button (click)="closePropagationModal()" type="submit"-->
<!-- class="uk-button uk-padding-small uk-padding-remove-vertical uk-button-default">-->
<!-- <span>Cancel</span>-->
<!-- </button>-->
<!-- <button (click)="confirmedPropagation()" type="submit"-->
<!-- class="uk-button uk-padding-small uk-padding-remove-vertical uk-margin-left uk-button-primary">-->
<!-- <span>Continue</span>-->
<!-- </button>-->
<!-- </div>-->
</modal-alert>
<fs-modal #propagationFsModal classTitle="uk-tile-default uk-border-bottom">
@ -364,12 +320,9 @@ export class OrcidWorkComponent {
public window: any;
public isLoggedIn: boolean = false;
public hasConsent: boolean = false;
public currentAction: string = "";
public hoverAdd: boolean = false;
public hoverDelete: boolean = false;
public properties: EnvProperties = properties;
public openaireEntities = OpenaireEntities;
@ -377,7 +330,7 @@ export class OrcidWorkComponent {
private _router: Router,
private orcidService: OrcidService,
private resultLandingService: ResultLandingService,
private userManagementService: UserManagementService) {
private userManagementService: UserManagementService, private _logService: LogService, private _userProfileService: UserProfileService) {
if (typeof document !== 'undefined') {
this.tokenUrl = properties.orcidTokenURL
+ "client_id=" + properties.orcidClientId
@ -389,18 +342,27 @@ export class OrcidWorkComponent {
}
ngOnInit() {
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
if (user) {
this.isLoggedIn = true;
if (!this.givenPutCode) {
this.getPutCode();
if(this.properties.environment != 'beta') {
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
if (user) {
this.isLoggedIn = true;
if (!this.givenPutCode) {
this.getPutCode();
}
} else {
this.isLoggedIn = false;
}
} else {
}, error => {
this.isLoggedIn = false;
}));
if (properties.dashboard == 'irish') {
this.subscriptions.push(this._userProfileService.getUserProfile().subscribe(userProfile => {
this.hasConsent = userProfile.consent;
}, error => {
this.hasConsent = false;
}));
}
}, error => {
this.isLoggedIn = false;
}));
}
}
ngOnDestroy() {
@ -441,7 +403,7 @@ export class OrcidWorkComponent {
}
openGrantWindow() {
if (!Session.isLoggedIn()) {
if (!this.isLoggedIn) {
//this.userValidMessage = "User session has expired. Please login again.";
this._router.navigate(['/user-info'], {
queryParams: {
@ -513,7 +475,7 @@ export class OrcidWorkComponent {
}
public saveWorkPreparation() {
if (!Session.isLoggedIn()) {
if (!this.isLoggedIn) {
//this.userValidMessage = "User session has expired. Please login again.";
this._router.navigate(['/user-info'], {
queryParams: {
@ -522,7 +484,15 @@ export class OrcidWorkComponent {
}
});
} else {
if(properties.dashboard == 'irish' && !this.hasConsent){
this._router.navigate(['/consent'], {
queryParams: {
"errorCode": LoginErrorCodes.NOT_VALID,
"redirectUrl": this._router.url
}
});
return;
}
if (this.requestGrant) {
this.openGrantModal("Add, delete or edit work in your ORCID record");
} else {
@ -540,7 +510,9 @@ export class OrcidWorkComponent {
private saveWork() {
this.subscriptions.push(this.orcidService.saveWork(this.resultLandingInfo, this.pids).subscribe(
response => {
if(this.properties.logServiceUrl) {
this.subscriptions.push(this._logService.logOrcidLink(this.properties, "added", this.resultLandingInfo.title, this.resultLandingInfo.identifiers.get('doi')[0]).subscribe(res => { }));
}
// for testing only
// this.openGrantModal("Add work in your ORCID record");
// this.requestGrant = true;
@ -722,6 +694,9 @@ export class OrcidWorkComponent {
if (deletedPutCodes) {
for (let i = 0; i < deletedPutCodes.length; i++) {
let deletedPutCode = deletedPutCodes[i];
if(this.properties.logServiceUrl) {
this.subscriptions.push(this._logService.logRemoveOrcidLink(this.properties, deletedPutCode).subscribe(res => { }));
}
if (deletedPutCode == null) {
deletedAll = false;
} else {
@ -873,12 +848,17 @@ export class OrcidWorkComponent {
this.showLoading = false;
}
get tooltipBETA() {
// return "Login to the production environment to add works to your <span class=\"text-orcid\">ORCID</span> record";
return "Add or delete a work from your <span class=\"text-orcid\">ORCID</span> record. This feature is not available on BETA.";
}
get tooltipAdd() {
return "Add this work to your <span class=\"text-orcid\">ORCID</span> record" + ((properties.environment == "beta") ? ". The action will affect your real ORCID iD." : "");
return (properties.environment == "beta") ? this.tooltipBETA : ("Add this work to your <span class=\"text-orcid\">ORCID</span> record" + ((properties.environment == "test") ? ". The action will affect your real ORCID iD." : ""));
}
get tooltipDelete() {
return "Delete this work from your <span class=\"text-orcid\">ORCID</span> record" + ((properties.environment == "beta") ? ". The action will affect your real ORCID iD." : "");
return "Delete this work from your <span class=\"text-orcid\">ORCID</span> record" + ((properties.environment == "test") ? ". The action will affect your real ORCID iD." : "");
}
get tooltipNoPid() {
@ -886,16 +866,14 @@ export class OrcidWorkComponent {
}
get tooltipNoLoggedInUser() {
return "Add or delete a work from your <span class=\"text-orcid\">ORCID</span> record. Please log in first."
return (properties.environment == "beta") ? this.tooltipBETA : "Add or delete a work from your <span class=\"text-orcid\">ORCID</span> record. Please log in first."
}
hoverEvent($event, action: string = "add") {
if (action == "add") {
this.hoverAdd = $event.type == "mouseover";
this.hoverDelete = false;
} else if (action == "delete") {
this.hoverDelete = $event.type == "mouseover";
this.hoverAdd = false;
}
get isDisabled() {
return (this.properties.environment == 'beta' || this.showLoading || !this.isLoggedIn || (!this.pids && (!this.identifiers || this.identifiers.size == 0)));
}
get noPids() {
return (!this.pids && (!this.identifiers || this.identifiers.size == 0));
}
}

View File

@ -16,11 +16,12 @@ import {IconsModule} from '../utils/icons/icons.module';
import {IconsService} from "../utils/icons/icons.service";
import {orcid_add, orcid_bin} from "../utils/icons/icons";
import {FullScreenModalModule} from "../utils/modal/full-screen-modal/full-screen-modal.module";
import {LogServiceModule} from "../utils/log/LogService.module";
@NgModule({
imports: [
CommonModule, RouterModule, AlertModalModule, LoadingModule, ResultLandingUtilsModule,
IconsModule, FullScreenModalModule
IconsModule, FullScreenModalModule, LogServiceModule
],
declarations: [
OrcidComponent,

View File

@ -15,7 +15,7 @@ export class ReloadComponent {
}
public ngOnInit() {
let URL = Session.getReloadUrl();
let URL = Session.popReloadUrl();
if (URL && URL["path"] && URL["path"] != "") {
let url: string = URL["path"];
let host = URL["host"];
@ -24,14 +24,12 @@ export class ReloadComponent {
let baseUrl = (document && document.getElementsByTagName('base')) ? document.getElementsByTagName('base')[0].href.split(document.location.host)[1] : "/";
url = (baseUrl.length > 1 && url.indexOf(baseUrl) != -1) ? ("/" + url.split(baseUrl)[1]) : url;
if (paramsObject) {
Session.setReloadUrl("", "", "", "");
if(URL['fragment'] && URL['fragment'] !== '') {
this._router.navigate([url], {queryParams: paramsObject, fragment: URL['fragment']});
} else {
this._router.navigate([url], {queryParams: paramsObject});
}
} else {
Session.setReloadUrl("", "", "", "");
if(URL['fragment'] && URL['fragment'] !== '') {
this._router.navigate([url], {fragment: URL['fragment']});
} else {
@ -39,12 +37,10 @@ export class ReloadComponent {
}
}
} else {
Session.setReloadUrl("", "", "", "");
window.location.href = host + url + ((URL["params"] && URL["params"] != null) ? ((URL["params"].indexOf("?") == -1 ? "?" : "") + URL["params"]) : "");
}
} else {
Session.setReloadUrl("", "", "", "");
this._router.navigate(['/']);
}
}

View File

@ -1,16 +1,17 @@
import {AfterViewInit, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewChild} from "@angular/core";
import {User} from "../login/utils/helper.class";
import {AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, ViewChild} from "@angular/core";
import {Role, User} from "../login/utils/helper.class";
import {ActivatedRoute, Router} from "@angular/router";
import {UserManagementService} from "../services/user-management.service";
import {UserRegistryService} from "../services/user-registry.service";
import {LoginErrorCodes} from "../login/utils/guardHelper.class";
import {Subscriber, Subscription} from "rxjs";
import {Subscription} from "rxjs";
import {UntypedFormBuilder, UntypedFormControl, Validators} from "@angular/forms";
import {AlertModal} from "../utils/modal/alert";
import {properties} from "../../../environments/environment";
import {EmailService} from "../utils/email/email.service";
import {Composer} from "../utils/email/composer";
import {ClearCacheService} from "../services/clear-cache.service";
import {BaseComponent} from "../sharedComponents/base/base.component";
@Component({
selector: 'role-verification',
@ -77,15 +78,17 @@ import {ClearCacheService} from "../services/clear-cache.service";
</modal-alert>
`
})
export class RoleVerificationComponent implements OnInit, OnDestroy, AfterViewInit {
export class RoleVerificationComponent extends BaseComponent implements OnInit, AfterViewInit {
@Input()
public id: string;
@Input()
public type: string;
set type(type: string) {
this._type = Role.GROUP + type;
}
@Input()
public name: string;
@Input()
public service: "connect" | "monitor" = "monitor";
public service: "connect" | "monitor" | "irish" = "monitor";
@Input()
public userInfoLinkPrefix = '';
@Input()
@ -93,7 +96,7 @@ export class RoleVerificationComponent implements OnInit, OnDestroy, AfterViewIn
public user: User;
public verification: any;
public code: UntypedFormControl;
private subscriptions: any[] = [];
private _type: string;
private paramsSubscription: Subscription;
@ViewChild('managerModal') managerModal: AlertModal;
@ViewChild('memberModal') memberModal: AlertModal;
@ -102,14 +105,15 @@ export class RoleVerificationComponent implements OnInit, OnDestroy, AfterViewIn
public loading: boolean = false;
public isMember: boolean = false;
constructor(private route: ActivatedRoute,
private router: Router,
constructor(protected _route: ActivatedRoute,
protected _router: Router,
private fb: UntypedFormBuilder,
private emailService: EmailService,
private userManagementService: UserManagementService,
private userRegistryService: UserRegistryService,
private clearCacheService: ClearCacheService,
private cdr: ChangeDetectorRef) {
super();
}
ngOnInit() {
@ -119,14 +123,14 @@ export class RoleVerificationComponent implements OnInit, OnDestroy, AfterViewIn
ngAfterViewInit() {
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
this.user = user;
this.paramsSubscription = this.route.queryParams.subscribe(params => {
this.paramsSubscription = this._route.queryParams.subscribe(params => {
if (params) {
this.cdr.detectChanges();
if(params['verify'] && !this.isMember) {
if (this.user) {
this.subscriptions.push(this.userRegistryService.getInvitation(params['verify']).subscribe(verification => {
this.verification = verification;
if (this.user.email === this.verification.email.toLowerCase() && this.id === this.verification.entity && this.type === this.verification.type) {
if (this.user.email === this.verification.email.toLowerCase() && this.id === this.verification.entity && this._type === this.verification.type) {
if (this.verification.verificationType === 'manager') {
this.openManagerModal();
} else if (this.verification.verificationType === 'member') {
@ -141,12 +145,12 @@ export class RoleVerificationComponent implements OnInit, OnDestroy, AfterViewIn
this.openErrorModal();
}));
} else {
this.router.navigate(['user-info'], {
this._router.navigate(['user-info'], {
queryParams: {
'errorCode': LoginErrorCodes.NOT_LOGIN,
'redirectUrl': this.router.url
'redirectUrl': this._router.url
},
relativeTo: this.route
relativeTo: this._route
});
}
} else if(this.isMember) {
@ -161,14 +165,10 @@ export class RoleVerificationComponent implements OnInit, OnDestroy, AfterViewIn
}
ngOnDestroy() {
super.ngOnDestroy();
if (this.paramsSubscription instanceof Subscription) {
this.paramsSubscription.unsubscribe();
}
this.subscriptions.forEach(subscription => {
if (subscription instanceof Subscriber) {
subscription.unsubscribe();
}
});
}
public openManagerModal() {
@ -216,9 +216,12 @@ export class RoleVerificationComponent implements OnInit, OnDestroy, AfterViewIn
if (this.paramsSubscription instanceof Subscription) {
this.paramsSubscription.unsubscribe();
}
if (this.service === "monitor") {
if(this.service === "irish") {
this.loading = false;
this.router.navigate(['/admin/' + this.verification.entity]);
this.userManagementService.login(properties.domain + '/admin/' + this.verification.entity);
} else if (this.service === "monitor" ) {
this.loading = false;
this._router.navigate(['/admin/' + this.verification.entity]);
} else {
this.subscriptions.push(this.emailService.notifyManagers(this.id, 'manager',
Composer.composeEmailToInformOldManagersForTheNewOnes(this.name, this.id)).subscribe(() => {
@ -275,6 +278,6 @@ export class RoleVerificationComponent implements OnInit, OnDestroy, AfterViewIn
cancel() {
this.isMember = false;
this.router.navigate([]);
this._router.navigate([]);
}
}

View File

@ -1,4 +1,5 @@
@import (reference) "~src/assets/openaire-theme/less/_import-variables";
@import (optional) "~src/assets/extend-theme/less/_import-variables";
@sdgs: #E6233D, #DF9F00, #19A220, #D70023, #FF0B00, #00BFE8, #FFC300, #B10240, #FF5D00,
#F50D86, #FF8A00, #CA8A03, #2B772B, #0098DF, #00B91C, #0069A2, #1C336A;

View File

@ -53,8 +53,15 @@ import {RefineFieldResultsService} from "../services/refineFieldResults.service"
export class SearchDataProvidersComponent {
private errorCodes: ErrorCodes;
private errorMessages: ErrorMessagesComponent;
@Input() customFilter:SearchCustomFilter= null;
@Input() tableViewLink;
@Input() customFilters: SearchCustomFilter[] = null;
@Input()
set customFilter(customFilter: SearchCustomFilter | SearchCustomFilter[]) {
if(!Array.isArray(customFilter)) {
this.customFilters = customFilter?[customFilter]:null;
}else{
this.customFilters = customFilter;
}
} @Input() tableViewLink;
@Input() searchForm: SearchForm = {class: 'search-form', dark: true};
public results =[];
public filters =[];
@ -159,7 +166,7 @@ export class SearchDataProvidersComponent {
this.searchPage.keywordFields = this.searchFields.DEPOSIT_DATASOURCE_KEYWORD_FIELDS;
this.searchPage.usedBy = "deposit";
}
this.searchPage.prepareSearchPage(this.fieldIds, this.selectedFields, this.refineFields, [], this.staticFields, this.fieldIdsMap,this.customFilter,params, this.entityType);
this.searchPage.prepareSearchPage(this.fieldIds, this.selectedFields, this.refineFields, [],this.staticFields, this.fieldIdsMap,this.customFilters,params, this.entityType);
if(refine) {
this._getFilters(this.searchPage.getSearchAPIQueryForAdvancedSearhFields(params), this.searchUtils.page, 0, true, this.searchPage.getSearchAPIQueryForRefineFields(params, firstLoad));
} else {
@ -381,7 +388,7 @@ export class SearchDataProvidersComponent {
}
let index: number = this.filters.findIndex((fltr: Filter) => fltr.filterId == filter.filterId);
filter.isOpen = true;
filter.isViewAllOpen = true;
this.filters[index] = filter;
this.cdr.detectChanges();
},

View File

@ -62,8 +62,15 @@ export class SearchOrganizationsComponent {
public loadPaging: boolean = true;
public oldTotalResults: number = 0;
public pagingLimit: number = 0;
@Input() customFilter:SearchCustomFilter= null;
public refineFields: string[] = this.searchFields.ORGANIZATION_REFINE_FIELDS;
@Input() customFilters: SearchCustomFilter[] = null;
@Input()
set customFilter(customFilter: SearchCustomFilter | SearchCustomFilter[]) {
if(!Array.isArray(customFilter)) {
this.customFilters = customFilter?[customFilter]:null;
}else{
this.customFilters = customFilter;
}
} public refineFields: string[] = this.searchFields.ORGANIZATION_REFINE_FIELDS;
@ViewChild(NewSearchPageComponent, { static: true }) searchPage: NewSearchPageComponent;
@Input() simpleView: boolean = true;
@Input() simpleSearchLink: string = "";
@ -132,7 +139,7 @@ export class SearchOrganizationsComponent {
// this.searchPage.fieldIdsMap = this.fieldIdsMap;
// this.searchPage.customFilter = this.customFilter;
// this.searchPage.getSelectedFiltersFromUrl(params);
this.searchPage.prepareSearchPage(this.fieldIds, this.selectedFields, this.refineFields, [], [], this.fieldIdsMap,this.customFilter,params, "organization");
this.searchPage.prepareSearchPage(this.fieldIds, this.selectedFields, this.refineFields, [], [], this.fieldIdsMap,this.customFilters,params, "organization");
if(refine) {
this._getFilters(this.searchPage.getSearchAPIQueryForAdvancedSearhFields(), this.searchUtils.page, 0, true, this.searchPage.getSearchAPIQueryForRefineFields(params, firstLoad));
} else {
@ -340,7 +347,7 @@ export class SearchOrganizationsComponent {
}
let index: number = this.filters.findIndex((fltr: Filter) => fltr.filterId == filter.filterId);
filter.isOpen = true;
filter.isViewAllOpen = true;
this.filters[index] = filter;
this.cdr.detectChanges();
},

View File

@ -46,8 +46,15 @@ import {RefineFieldResultsService} from "../services/refineFieldResults.service"
export class SearchProjectsComponent {
private errorCodes: ErrorCodes;
private errorMessages: ErrorMessagesComponent;
@Input() customFilter: SearchCustomFilter = null;
@Input() searchForm: SearchForm = {class: 'search-form', dark: true};
@Input() customFilters: SearchCustomFilter[] = null;
@Input()
set customFilter(customFilter: SearchCustomFilter | SearchCustomFilter[]) {
if(!Array.isArray(customFilter)) {
this.customFilters = customFilter?[customFilter]:null;
}else{
this.customFilters = customFilter;
}
} @Input() searchForm: SearchForm = {class: 'search-form', dark: true};
public results = [];
public filters = [];
public rangeFilters: RangeFilter[] = [];
@ -133,7 +140,7 @@ export class SearchProjectsComponent {
this.searchPage.fieldIds = this.fieldIds;
this.selectedFields = [];
this.searchPage.prepareSearchPage(this.fieldIds, this.selectedFields, this.refineFields, this.rangeFields, [], this.fieldIdsMap, this.customFilter, params, "project");
this.searchPage.prepareSearchPage(this.fieldIds, this.selectedFields, this.refineFields, this.rangeFields,[], this.fieldIdsMap, this.customFilters, params, "project");
if (refine) {
this._getFilters(this.searchPage.getSearchAPIQueryForAdvancedSearhFields(), this.searchUtils.page, 0, "", true, this.searchPage.getSearchAPIQueryForRangeFields(params) + this.searchPage.getSearchAPIQueryForRefineFields(params, firstLoad));
} else {
@ -369,7 +376,7 @@ export class SearchProjectsComponent {
}
let index: number = this.filters.findIndex((fltr: Filter) => fltr.filterId == filter.filterId);
filter.isOpen = true;
filter.isViewAllOpen = true;
this.filters[index] = filter;
this.cdr.detectChanges();
},

View File

@ -19,7 +19,7 @@ import {RefineFieldResultsService} from "../services/refineFieldResults.service"
selector: 'search-research-results',
template: `
<new-search-page
pageTitle="{{(simpleView?'':'Advanced ')}} Search for {{ getEntityName(resultType, true, true) | titlecase }}"
pageTitle="{{pageTitlePrefix}}{{(simpleView?'':'Advanced ')}} Search for {{ getEntityName(resultType, true, true) | titlecase }}"
[entityType]="resultType"
[type]="getEntityName(resultType, true, true)"
[results]="results"
@ -83,7 +83,15 @@ export class SearchResearchResultsComponent {
public loadPaging: boolean = true;
public oldTotalResults: number = 0;
@Input() openaireLink: string = null;
@Input() customFilter: SearchCustomFilter = null;
@Input() customFilters: SearchCustomFilter[] = null;
@Input()
set customFilter(customFilter: SearchCustomFilter | SearchCustomFilter[]) {
if(!Array.isArray(customFilter)) {
this.customFilters = customFilter?[customFilter]:null;
}else{
this.customFilters = customFilter;
}
}
public pagingLimit: number = 0;
properties: EnvProperties = properties;
public openaireEntities = OpenaireEntities;
@ -113,7 +121,7 @@ export class SearchResearchResultsComponent {
@Input() usedBy: string = "search";
@Input() orcidQuery: string = "";
@Input() identifiers: string[] = [];
@Input() pageTitlePrefix: string = "";
private refineQuery: string = "";
constructor(private route: ActivatedRoute, private _router: Router,
@ -183,7 +191,8 @@ export class SearchResearchResultsComponent {
this.searchUtils.sortBy = "";
}
this.selectedFields = [];
this.searchPage.prepareSearchPage(this.fieldIds, this.selectedFields, this.refineFields, this.rangeFields, this.staticFields, this.fieldIdsMap,this.customFilter,params, this.resultType, this.quickFilter);
this.searchPage.prepareSearchPage(this.fieldIds, this.selectedFields, this.refineFields, this.rangeFields, this.staticFields, this.fieldIdsMap,this.customFilters,params, this.resultType, this.quickFilter);
if(refine) {
this._getFilters(this.searchPage.getSearchAPIQueryForAdvancedSearhFields(), this.searchUtils.page, 0, "", true, this.searchPage.getSearchAPIQueryForRangeFields(params)+this.searchPage.getSearchAPIQueryForRefineFields(params, firstLoad));
} else {
@ -309,7 +318,14 @@ export class SearchResearchResultsComponent {
}
if (refine) {
this.filters = this.searchPage.prepareFiltersToShow(filters, totalResults);
let refineFilters = this.searchPage.prepareFiltersToShow(filters, totalResults);
// for(let filter of refineFilters) {
// let index = this.filters.findIndex(oldFilter => oldFilter.filterId == filter.filterId);
// if(index != -1 && this.filters[index].isOpen) {
// filter.isOpen = true;
// }
// }
this.filters = refineFilters;
this.rangeFilters = this.searchPage.prepareRangeFiltersToShow();
this.staticFilters = this.searchPage.prepareStaticFiltersToShow();
@ -319,9 +335,14 @@ export class SearchResearchResultsComponent {
if (group.type == "refine") {
let groupedFilters = {title: group.title, values: []};
for (let field of group.values) {
let index = this.filters.findIndex(filter => filter.filterId == field);
let index = this.staticFilters.findIndex(filter => filter.filterId == field);
if (index > -1) {
groupedFilters.values.push(this.filters[index]);
groupedFilters.values.push(this.staticFilters[index]);
} else {
index = this.filters.findIndex(filter => filter.filterId == field);
if (index > -1) {
groupedFilters.values.push(this.filters[index]);
}
}
}
if (groupedFilters.values.length > 0) {
@ -526,7 +547,7 @@ export class SearchResearchResultsComponent {
}
let index: number = this.filters.findIndex((fltr: Filter) => fltr.filterId == filter.filterId);
filter.isOpen = true;
filter.isViewAllOpen = true;
filter.countSelectedValues = oldFilter.countSelectedValues;
filter.radioValue = oldFilter.radioValue;
this.filters[index] = filter;

View File

@ -106,7 +106,8 @@ export class EntitiesSelectionComponent {
}));
} else if ((this.customFilter && this.customFilter.queryFieldName == "community") ||
(this.customFilter && (this.customFilter.queryFieldName == "relfunder" || this.customFilter.queryFieldName == "funder")) ||
(this.customFilter && this.customFilter.queryFieldName == "relorganizationid")) {
(this.customFilter && this.customFilter.queryFieldName == "relorganizationid")
|| this.properties.dashboard == "irish") {
this.entities.push({label: OpenaireEntities.RESULTS, value: 'result'});
this.entities.push({label: OpenaireEntities.PROJECTS, value: 'project'});
this.entities.push({label: OpenaireEntities.ORGANIZATIONS, value: 'organization'});

View File

@ -4,15 +4,15 @@
<div [class.uk-invisible]="list.children.length === 0" class="uk-position-relative">
<div class="uk-slider-container">
<ul #list class="uk-slider-items uk-grid uk-grid-small uk-margin-small-right uk-flex-nowrap" style="padding-bottom: 1px">
<ng-container *ngIf="customFilter && ((customFilterEnabled &&
refineFields.indexOf(customFilter.queryFieldName) ==
-1) ||customFilter.isHiddenFilter)">
<ng-container *ngFor="let customFilter of customFilters">
<ng-container *ngIf="customFilter.isHiddenFilter">
<li class="uk-flex uk-flex-middle">
<span class="uk-label uk-label-secondary uk-text-truncate">
{{customFilter.valueName}}
</span>
</li>
</ng-container>
</ng-container>
<ng-container *ngIf="resultTypes && resultTypes.countSelectedValues > 0">
<ng-container *ngFor="let type of resultTypes.values; let i = index;">
<ng-container *ngIf="type.selected">
@ -49,9 +49,13 @@
<span class="uk-label uk-label-primary uk-flex uk-flex-middle">
<span
class="uk-margin-small-right uk-width-expand uk-text-truncate">
<span *ngIf="filter.type && filter.type == 'boolean' else noboolean">{{filter.title}}:
{{value.name=='true'?'Yes':'No'}}
</span>
<ng-container *ngIf="filter.type && (filter.type == 'boolean' || filter.type == 'triplet') else noboolean">
<!-- *ngIf="filter.type == 'boolean'"-->
<span>{{filter.title}}:
{{(value.name=='true'||value.name=='Yes')?'Yes':'No'}}
</span>
<!-- <span *ngIf="filter.type == 'triplet'">{{value.name=='true'?'':'Not '}}{{filter.title}}</span>-->
</ng-container>
<ng-template #noboolean>
{{value.name}}
</ng-template></span>
@ -66,14 +70,16 @@
<ng-container *ngFor="let filter of filters ">
<ng-container *ngIf="filter.countSelectedValues > 0">
<ng-container *ngFor="let value of getSelectedValues(filter); let i = index; let end = last; ">
<li *ngIf="!customFilter || (customFilter.isHiddenFilter && customFilter.valueId != value.id)"
<li *ngIf="!customFilters || (customFilters[0].isHiddenFilter && customFilters[0].valueId != value.id)"
class="">
<span class="uk-label uk-label-primary uk-flex uk-flex-middle">
<span
class="uk-margin-small-right uk-width-expand uk-text-truncate">
<span *ngIf="filter.type && filter.type == 'boolean' else noboolean">{{filter.title}}:
{{value.name=='true'?'Yes':'No'}}
</span>
<ng-container *ngIf="filter.type && (filter.type == 'boolean' || filter.type == 'triplet') else noboolean">
<span>{{filter.title}}:
{{(value.name=='true'||value.name=='Yes')?'Yes':'No'}}
</span>
</ng-container>
<ng-template #noboolean>
{{value.name}}
</ng-template></span>
@ -94,13 +100,13 @@
</h1>
</ng-template>
<ng-template #search_filter let-filter="filter" let-showResultCount="showResultCount">
<ng-template #search_filter let-filter="filter" let-showResultCount="showResultCount" let-grouped="grouped">
<search-filter [filterValuesNum]="filterValuesNum" [showMoreInline]="showMoreFilterValuesInline"
[isDisabled]="disabled"
[filter]="filter" [showResultCount]=showResultCount
(onFilterChange)="filterChanged($event)"
(onFilterToggle)="filterToggled($event)"
[actionRoute]="true"></search-filter>
[actionRoute]="true" [grouped]="grouped"></search-filter>
</ng-template>
<ng-template #filters_column>
@ -125,15 +131,15 @@
<ul *ngIf="!showUnknownFilters" class="uk-list uk-list-xlarge">
<ng-container *ngIf="orderedFilters && orderedFilters.length > 0">
<ng-container *ngFor="let group of orderedFilters">
<h5 *ngIf="group.title" class="uk-h5">{{group.title}}</h5>
<ng-container *ngFor="let filter of group.values">
<h6 *ngIf="group.title" class="uk-h6">{{group.title}}</h6>
<ng-container *ngFor="let filter of group.values; let i=index">
<li *ngIf="filter.originalFilterIdFrom && filter.originalFilterIdTo; else refineBlock">
<range-filter [isDisabled]="disabled" [filter]="filter"
(onFilterChange)="filterChanged($event)" [actionRoute]="true"></range-filter>
</li>
<ng-template #refineBlock>
<li *ngIf="filter.values && filter.values.length > 0">
<ng-container *ngTemplateOutlet="search_filter; context: {filter: filter, showResultCount: filter.type!='static'}"></ng-container>
<li *ngIf="filter.values && filter.values.length > 0" [ngClass]="group.title && i==0 ? 'uk-margin-small-top' : ''">
<ng-container *ngTemplateOutlet="search_filter; context: {filter: filter, showResultCount: filter.type!='static', grouped: !!group.title}"></ng-container>
</li>
</ng-template>
</ng-container>
@ -222,12 +228,13 @@
</div>
<!-- TODO - Clean up -->
<div *ngIf="!includeOnlyResultsAndFilter" [class]="usedBy != 'deposit' && usedBy != 'orcid' && (!customFilter || customFilter.queryFieldName != 'communityId') ?
<div *ngIf="!includeOnlyResultsAndFilter" [class]="usedBy != 'deposit' && usedBy != 'orcid' &&
(!customFilters || (customFilters && customFilters[0].queryFieldName != 'communityId')) ?
(stickyForm?'':' ') :
(+ (stickyForm?'':' uk-section') +' uk-padding-remove-bottom uk-padding-remove-top ' +
((usedBy == 'deposit' || usedBy == 'orcid') ? ' uk-padding-remove-top ' : ' '))"
[attr.uk-sticky]="((stickyForm || (simpleView && mobile))?'{offset:100;start:90;cls-active:uk-active uk-sticky-below;cls-inactive:uk-sticky '+
(usedBy != 'deposit' && usedBy != 'orcid' && (!customFilter || customFilter.queryFieldName != 'communityId') ?
(usedBy != 'deposit' && usedBy != 'orcid' && (!customFilters || customFilters[0].queryFieldName != 'communityId') ?
' uk-position-relative ' :(' uk-section ' ))+'}':null)">
<div class="uk-background-norepeat uk-background-bottom-center" [ngClass]="searchForm.class">
<div class="uk-width-1-1">
@ -254,7 +261,7 @@
[advancedSearchLinkParameters]="this.routerHelper.createQueryParams(this.parameterNames, this.parameterValues)"
[simpleView]="simpleView" [formPlaceholderText]="formPlaceholderText" [isMobile]="mobile"
[resultTypes]="resultTypes" [quickFilter]="quickFilter" [entitiesSelection]="entitiesSelection"
[showSwitchSearchLink]="showSwitchSearchLink" [customFilter]="customFilter"
[showSwitchSearchLink]="showSwitchSearchLink" [customFilter]="customFilters?customFilters[0]:null"
>
</advanced-search-form>
</div>
@ -317,18 +324,13 @@
</div>
<div class="uk-width-expand@m uk-with-1-1@s">
<div [class.uk-padding-small]="mobile" class="uk-padding-remove-vertical">
<!-- Results only for Custom Filter? -->
<div *ngIf="customFilter && !customFilter.isHiddenFilter &&
customFilter.selected == false && customFilter.promptToAddFilter"
class="uk-alert uk-animation-slide-top-small">
Do you want to see results only for {{customFilter.valueName}}? <a
(click)="addCustomFilter();">Click here</a>.
</div>
<!-- Related results for Custom Filter Alert -->
<div *ngIf="openaireLink && (searchUtils.totalResults > 0 || !loadPaging )"
class="uk-alert uk-margin-small-top ">
<span *ngIf="customFilter">The following results are related to <span class="uk-text-primary uk-text-bold"
<ng-container *ngFor="let customFilter of customFilters">
<span *ngIf="customFilter.showFilterMessage">The following results are related to <span class="uk-text-primary uk-text-bold"
>{{customFilter.valueName}}</span>.</span>
</ng-container>
Are you interested to view more results? Visit
<a
class="uk-margin-top uk-link"
@ -338,7 +340,7 @@
<div class="uk-flex uk-flex-middle uk-flex-wrap uk-child-width-1-1 uk-child-width-auto@m" [class.uk-flex-between]="!mobile"
[class.uk-margin-top]="mobile">
<!-- Total results, number of pages -->
<div class="uk-width-xlarge@m uk-margin-remove-bottom uk-text-truncate uk-margin-medium-right" [class.uk-h6]="!mobile" [class.uk-text-center]="mobile">
<div class="uk-width-expand@m uk-margin-remove-bottom uk-text-truncate" [class.uk-margin-medium-right]="!mobile" [class.uk-h6]="!mobile" [class.uk-text-center]="mobile">
<ng-container *ngIf="results && searchUtils.totalResults > 0">
<span>{{searchUtils.totalResults|number}}</span>
<span class="uk-text-meta uk-text-capitalize"> {{type}}</span>
@ -365,6 +367,11 @@
[isDisabled]="disabled"
[type]="csvPath" [csvParams]="csvParams" [totalResults]="searchUtils.totalResults">
</search-download>
<ng-container *ngIf="properties.zenodoDumpUrl && entityType == 'result'">
<a [href]="properties.zenodoDumpUrl" target="_blank" class=" uk-margin-left uk-button uk-button-link uk-flex uk-flex-middle">
<img src="assets/common-assets/common/zenodoDump.png" width="20"><span class="uk-margin-xsmall-left">Data dump</span>
</a>
</ng-container>
</div>
</div>
<div *ngIf="(searchUtils.status !== errorCodes.LOADING || !loadPaging) && !mobile" class="uk-margin-top">
@ -444,4 +451,3 @@
<div *ngIf="isMobile || isServer" class="uk-hidden@m">
<ng-container *ngTemplateOutlet="main; context: {mobile: true}"></ng-container>
</div>
<modal-alert #removeCustomFilter (alertOutput)="closeCustomFilterModal()"></modal-alert>

View File

@ -69,7 +69,15 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
@Input() loadPaging: boolean = true;
@Input() oldTotalResults: number = 0;
@Input() openaireLink: string;
@Input() customFilter: SearchCustomFilter;
@Input() customFilters: SearchCustomFilter[] = null;
@Input()
set customFilter(customFilter: SearchCustomFilter | SearchCustomFilter[]) {
if(!Array.isArray(customFilter)) {
this.customFilters = customFilter?[customFilter]:null;
}else{
this.customFilters = customFilter;
}
}
@Input() sort: boolean = true;
@Input() sortedByChanged: string = "";
@Input() searchForm: SearchForm = {class: 'search-form', dark: true};
@ -144,11 +152,9 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
//Dashboard
filterToggle = false;
customFilterEnabled: boolean = false;
//stickyform
@Input() stickyForm: boolean = false;
@ViewChild('removeCustomFilter') removeCustomFilter: AlertModal;
currentValueToRemove;
currentFilterToRemove;
public indexUpdateDate: Date;
@ -195,7 +201,6 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
if (params['page'] && params['page'] != 1) {
HelperFunctions.scrollToId("searchForm");
}
this.customFilterEnabled = params['cf'] && params['cf'] == "true";
if (this.basicMetaDescription.length == 0) {
if (this.entityType == "result") {
this.basicMetaDescription = [OpenaireEntities.RESULTS, "Discover" + (this.properties.adminToolsCommunity == 'openaire' ? " over 100 million of" : "") + " "+OpenaireEntities.RESULTS+" ", "categorized by research type, year range, funder, languages, "+OpenaireEntities.COMMUNITY+" and "+OpenaireEntities.DATASOURCES+"."];
@ -229,14 +234,14 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
}
private getPageContents() {
this.subscriptions.push(this.helper.getPageHelpContents(this.properties, (this.customFilter && this.customFilter.queryFieldName == "communityId") ? this.customFilter.valueId : this.properties.adminToolsCommunity, this.router.url).subscribe(contents => {
this.subscriptions.push(this.helper.getPageHelpContents(this.properties, (this.customFilters && this.customFilters[0] && this.customFilters[0].queryFieldName == "communityId") ? this.customFilters[0].valueId : this.properties.adminToolsCommunity, this.router.url).subscribe(contents => {
this.pageContents = contents;
}));
}
private getDivContents() {
this.subscriptions.push(this.helper.getDivHelpContents(this.properties, (this.customFilter && this.customFilter.queryFieldName == "communityId") ? this.customFilter.valueId : this.properties.adminToolsCommunity, this.router.url).subscribe(contents => {
this.subscriptions.push(this.helper.getDivHelpContents(this.properties, (this.customFilters && this.customFilters[0] && this.customFilters[0].queryFieldName == "communityId") ? this.customFilters[0].valueId : this.properties.adminToolsCommunity, this.router.url).subscribe(contents => {
this.divContents = contents;
}));
}
@ -422,13 +427,19 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
/*
* Get A sub-array of this.refineFields array, which contains the ids of the selected filters
*/
public getSelectedFilters(): string[] {
var selected: string[] = [];
public getSelectedFilters(): Map<string, string[]> {
var selected: Map<string, string[]> = new Map<string, string[]>();
var filters = this.URLCreatedFilters;
for (var i = 0; i < filters.length; i++) {
var filter: Filter = filters[i];
if (filter.countSelectedValues > 0) {
selected.push(filter.filterId);
let selectedValues: string[] = [];
for(let value of filter.values) {
if(value.selected) {
selectedValues.push(value.id);
}
}
selected.set(filter.filterId, selectedValues);
}
}
return selected;
@ -437,11 +448,63 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
/*
* Get A sub-array of this.refineFields array, which contains the ids of the selected parameters
*/
private getSelectedParameters(): string[] {
var selected: string[] = [];
private getSelectedParameters(): Map<string, string[]> {
var selected: Map<string, string[]> = new Map<string, string[]>();
for (var i = 0; i < this.refineFields.length; i++) {
if (this.parameterNames.indexOf(this.refineFields[i]) != -1) {
selected.push(this.refineFields[i]);
let selectedValues: string[] = [];
let index = this.parameterNames.indexOf(this.refineFields[i]);
if (index != -1) {
for(let values of this.parameterValues[index]) {
let split = values.split('"');
for(let value of split) {
if(value != "" && value != ",") {
selectedValues.push(value);
}
}
}
selected.set(this.refineFields[i], selectedValues);
}
}
return selected;
}
/*
* Get A sub-array of this.staticFields array, which contains the ids of the selected filters
*/
public getSelectedStaticFilters(): Map<string, string[]> {
var selected: Map<string, string[]> = new Map<string, string[]>();
var filters = this.URLCreatedStaticFilters;
for (var i = 0; i < filters.length; i++) {
var filter: Filter = filters[i];
if (filter.countSelectedValues > 0) {
let selectedValues: string[] = [];
for(let value of filter.values) {
if(value.selected) {
selectedValues.push(value.id);
}
}
selected.set(filter.filterId, selectedValues);
}
}
return selected;
}
/*
* Get A sub-array of this.staticFields array, which contains the ids of the selected parameters
*/
private getSelectedStaticParameters(): Map<string, string[]> {
var selected: Map<string, string[]> = new Map<string, string[]>();
for (var i = 0; i < this.staticFields.length; i++) {
let selectedValues: string[] = [];
let index = this.parameterNames.indexOf(this.staticFields[i]);
if (index != -1) {
let split = this.parameterValues[index].split('"');
for(let value of split) {
if(value != "" && value != ",") {
selectedValues.push(value);
}
}
selected.set(this.staticFields[i], selectedValues);
}
}
return selected;
@ -451,17 +514,30 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
* Get A sub-array of this.refineFields array, which hides hidden fields (e.g Funding level 0,1,2,..), and contains those that depend on another fields (e.g Funding level 0 if Funder is selected )
*/
public getFields(): string[] {
var selected_filters: string[] = this.getSelectedFilters();
if (selected_filters.length == 0) {
var selected_filters: Map<string, string[]> = this.getSelectedFilters();
if (selected_filters.size == 0) {
selected_filters = this.getSelectedParameters();
}
var selected_static_filters: Map<string, string[]> = this.getSelectedStaticFilters();
if (selected_static_filters.size == 0) {
selected_static_filters = this.getSelectedStaticParameters();
}
let all_selected_filters = new Map([...selected_filters, ...selected_static_filters]);
// let all_selected_filters = selected_filters.concat(selected_static_filters);
var fields: string[] = [];
for (var i = 0; i < this.refineFields.length; i++) {
var dependentTo = this.searchFieldsHelper.DEPENDENT_FIELDS[this.refineFields[i]];
var dependentToValues = this.searchFieldsHelper.DEPENDENT_FIELDS_AND_VALUES[this.refineFields[i]];
// TODO check again the checks
//if filter is not marked as hidden OR it is hidden but it is dependent to a field that it IS selected
if (this.searchFieldsHelper.HIDDEN_FIELDS.indexOf(this.refineFields[i]) == -1
|| (selected_filters.indexOf(dependentTo) != -1) || (selected_filters.indexOf(this.refineFields[i]) != -1)
|| (all_selected_filters.has(this.refineFields[i]))
|| (!dependentTo && !dependentToValues)
|| (all_selected_filters.has(dependentTo))
|| (dependentToValues && (all_selected_filters.has(dependentToValues.field) && dependentToValues.values.some(dependentValue=> all_selected_filters.get(dependentToValues.field).includes(dependentValue))))
|| (this.resultTypes && this.resultTypes.filterId == dependentTo && this.resultTypes.countSelectedValues > 0)
// || (this.resultAccesses && this.resultAccesses.filterId == dependentTo && this.resultAccesses.countSelectedValues > 0)
) {
@ -490,8 +566,12 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
*/
public checkSelectedFilters(filters: Filter[]) {
this.filters = filters;
if (this.customFilter && !this.customFilter.isHiddenFilter) {
this.customFilter.selected = null;
if (this.customFilters) {
for (let customFilter of this.customFilters) {
if (!customFilter.isHiddenFilter) {
customFilter.selected = null;
}
}
}
for (var i = 0; i < filters.length; i++) {
var filter: Filter = filters[i];
@ -507,8 +587,10 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
}
}
if (this.customFilter) {
this.customFilter.selected = this.customFilter.queryFieldName == filter.filterId && values.indexOf(StringUtils.quote(this.customFilter.valueId)) != -1;
if (this.customFilters) {
for (let customFilter of this.customFilters) {
customFilter.selected = customFilter.queryFieldName == filter.filterId && values.indexOf(StringUtils.quote(customFilter.valueId)) != -1;
}
}
// } else if (this.quickFilter && this.quickFilter.filterId == filter.filterId && this.quickFilter.selected) {
// for (let filterValue of filter.values) {
@ -528,11 +610,6 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
// this.quickFilter.filter = filter;
// }
}
if (this.parameterNames.indexOf("cf") != -1 && this.parameterValues[this.parameterNames.indexOf("cf")] == "true") {
this.customFilter.selected = true;
} else if (this.customFilter && this.customFilter.selected == null) {
this.customFilter.selected = false;
}
this.filterFilterValues(this.filters);
if (!this.includeOnlyResultsAndFilter) {
this.updateMeta(this.pageTitle);
@ -720,10 +797,6 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
public removeFilter(value: Value, filter: Filter, forceRemove: boolean = false) {
this.currentValueToRemove = value;
this.currentFilterToRemove = filter;
if (!forceRemove && this.customFilter && this.customFilter.queryFieldName == filter.filterId && this.customFilter.valueId == value.id && this.customFilter.promptToAddFilter) {
this.openRemoveCustomFilterModal();
return;
}
filter.countSelectedValues--;
this.selectedFilters--;
if (value.selected == true) {
@ -738,19 +811,6 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
this.goTo(1);
}
public openRemoveCustomFilterModal() {
this.removeCustomFilter.alertTitle = 'Remove filter';
this.removeCustomFilter.message = 'You are about to see results that are not related to ' + this.customFilter.valueName + '. Do you want to proceed?';
this.removeCustomFilter.okButtonText = 'Yes';
this.removeCustomFilter.cancelButtonText = 'No';
this.removeCustomFilter.open();
}
closeCustomFilterModal() {
this.customFilterEnabled = false;
this.removeFilter(this.currentValueToRemove, this.currentFilterToRemove, true);
}
public removeRangeFilter(filter: RangeFilter) {
filter.selectedFromValue = null;
filter.selectedToValue = null;
@ -798,41 +858,6 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
}
}*/
/**
* Set selected the value of the custom filter.
*/
addCustomFilter() {
this.customFilter.selected = true;
this.customFilterEnabled = true;
if (this.refineFields.indexOf(this.customFilter.queryFieldName) != -1) {
let found = false;
for (let filter of this.filters) {
if (this.customFilter.queryFieldName == filter.filterId) {
for (let value of filter.values) {
if (value.id == this.customFilter.valueId) {
value.selected = true;
filter.countSelectedValues++;
found = true;
break;
}
}
//add filter when field exist in refine but not in the refine values
if (!found) {
filter.countSelectedValues++;
filter.values.push({
selected: true,
name: this.customFilter.valueName,
id: this.customFilter.valueId,
number: 0
});
}
break;
}
}
}
this.filterChanged(null);
}
// for loading
public openLoading() {
@ -1219,9 +1244,16 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
}
}
this.customFilterEnabled = URLparams["cf"] == "true";
if (this.customFilter && (this.customFilter.isHiddenFilter || this.customFilterEnabled)) {
allFqs += "&fq=" + StringUtils.URIEncode(this.customFilter.queryFieldName + " exact " + StringUtils.quote((this.customFilter.valueId)));
if (this.customFilters) {
for (let customFilter of this.customFilters) {
if (customFilter.isHiddenFilter) {
if(customFilter.customQuery){
allFqs += "&fq=" + StringUtils.URIEncode(customFilter.customQuery);
}else {
allFqs += "&fq=" + StringUtils.URIEncode(customFilter.queryFieldName + " exact " + StringUtils.quote((customFilter.valueId)));
}
}
}
}
/* if (this.quickFilter && this.entityType == "result") {
@ -1580,11 +1612,6 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
this.parameterValues.push(this.searchUtils.keyword);
}
if (this.customFilterEnabled) {
this.parameterNames.push("cf");
this.parameterValues.push("true");
}
// if (this.searchUtils.size != this.resultsPerPage) {
// // allLimits += ((allLimits.length == 0) ? '?' : '&') + 'size=' + this.searchUtils.size;
// this.parameterNames.push("size");
@ -1812,7 +1839,38 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
}
public prepareStaticFiltersToShow() {
this.staticFilters = RefineResultsUtils.parse(this.staticFieldValues, this.staticFields, this.entityType, "search", true);
var selected_filters: Map<string, string[]> = this.getSelectedFilters();
if (selected_filters.size == 0) {
selected_filters = this.getSelectedParameters();
}
var selected_static_filters: Map<string, string[]> = this.getSelectedStaticFilters();
if (selected_static_filters.size == 0) {
selected_static_filters = this.getSelectedStaticParameters();
}
let all_selected_filters = new Map([...selected_filters, ...selected_static_filters]);
// let all_selected_filters = selected_filters.concat(selected_static_filters);
let staticFields: string[] = [];
for (var i = 0; i < this.staticFields.length; i++) {
var dependentTo = this.searchFieldsHelper.DEPENDENT_FIELDS[this.staticFields[i]];
var dependentToValues = this.searchFieldsHelper.DEPENDENT_FIELDS_AND_VALUES[this.staticFields[i]];
// TODO check again the checks
//if filter is not marked as hidden OR it is hidden but it is dependent to a field that it IS selected
if (this.searchFieldsHelper.HIDDEN_FIELDS.indexOf(this.staticFields[i]) == -1
|| (all_selected_filters.has(this.staticFields[i]))
|| (!dependentTo && !dependentToValues)
|| (all_selected_filters.has(dependentTo))
|| (dependentToValues && (all_selected_filters.has(dependentToValues.field) && dependentToValues.values.some(dependentValue=> all_selected_filters.get(dependentToValues.field).includes(dependentValue))))
) {
staticFields.push(this.staticFields[i]);
}
}
this.staticFilters = RefineResultsUtils.parse(this.staticFieldValues, staticFields, this.entityType, "search", true);
this.checkSelectedStaticFilters(this.staticFilters);
this.countSelectedStaticFilters(this.staticFilters);
this.cdr.detectChanges();

View File

@ -1,5 +1,6 @@
@import (reference) "~src/assets/openaire-theme/less/color.less";
@import (reference) "~src/assets/openaire-theme/less/_import-variables.less";
@import (optional) "~src/assets/extend-theme/less/_import-variables.less";
.setType(@color, @position: left) {
border-@{position}: 4px solid fade(@color, 30%);

View File

@ -60,7 +60,7 @@ export class PortalSearchResultComponent implements OnInit{
hasPermission(result: CommunityInfo & StakeholderInfo) {
if(this.type === "community") {
return result.status === "all" || (result.status === "manager" && result.isManager);
return result.isPublic() || (result.isRestricted() && result.isManager);
} else if(this.type === "stakeholder") {
return result.visibility === "PUBLIC" || (result.visibility === "RESTRICTED" && (result.isManager || result.isMember)) ||
(result.visibility === "PRIVATE" && result.isManager);

View File

@ -14,12 +14,13 @@ import {properties} from "../../../../environments/environment";
@Component({
selector: 'search-download',
template: `
<button [attr.uk-tooltip]="'title: Download' + ((totalResults > csvLimit)?' the first 2000 ':' ') + 'results;'"
<button [attr.uk-tooltip]="'title: Download' + ((totalResults > csvLimit)?' the first 2000 ':' ') + 'results.' +
((totalResults > csvLimit && properties.zenodoDumpUrl)?' To get all results download the data dump. ':' ') "
class="uk-button uk-button-link uk-flex uk-flex-middle" [class.uk-disabled]="isDisabled"
[disabled]="isDisabled"
(click)="downloadfile(downloadURLAPI+'?format=csv'+csvParams,type+'-report-'+((totalResults > csvLimit)?'2000 ':totalResults))">
<icon name="download" [flex]="true"></icon>
<span class="uk-margin-small-left">Download Results</span>
<span class="uk-margin-xsmall-left">Download Results</span>
</button>
<modal-loading></modal-loading>
<modal-alert #AlertModalCsvError></modal-alert>

View File

@ -1,9 +1,35 @@
<div *ngIf="filter.values.length >0">
<div class="uk-flex uk-flex-middle uk-margin-bottom">
<div *ngIf="!grouped else groupedTitle" class="uk-flex uk-flex-middle uk-margin-bottom">
<h6 [title]="filter.title" class="uk-margin-remove-bottom">{{_formatTitle(filter.title, filter.values.length)}}</h6>
<a *ngIf="filter.countSelectedValues>0" class="uk-text-small uk-margin-left" (click)="clearFilter()" [class.uk-disabled]="isDisabled">Clear</a>
</div>
<div>
<ng-template #groupedTitle>
<div class="uk-flex uk-flex-middle uk-margin-small-bottom">
<div [title]="filter.title" class="uk-margin-remove-bottom uk-text-meta">{{_formatTitle(filter.title, filter.values.length)}}</div>
<a *ngIf="filter.countSelectedValues>0" class="uk-text-small uk-margin-left" (click)="clearFilter()" [class.uk-disabled]="isDisabled">Clear</a>
</div>
</ng-template>
<div *ngIf="filter.type && filter.type == 'triplet'">
<span>
<mat-button-toggle-group [name]="filter.title" [aria-label]="filter.title" class="uk-text-xsmall"
[(ngModel)]="filter.radioValue" (ngModelChange)="tripletFilterChange()" [disabled]="isDisabled">
<mat-button-toggle *ngFor="let value of filter.values" [value]="value.id" class="filter-button-toggle">
<div style="width: 50px">{{value.name}}</div>
</mat-button-toggle>
<!-- <ng-container *ngFor="let value of filter.values">-->
<!-- <mat-button-toggle value="true">-->
<!-- {{filter.title}}-->
<!-- </mat-button-toggle>-->
<!-- <mat-button-toggle value="false">-->
<!-- Not {{filter.title}}-->
<!-- </mat-button-toggle>-->
<!-- </ng-container>-->
</mat-button-toggle-group>
</span>
</div>
<div *ngIf="!filter.type || filter.type != 'triplet'">
<div *ngFor="let value of getSelectedAndTopValues(filter, filterValuesNum)"
class="uk-animation-fade uk-text-small uk-margin-small-bottom">
<div [title]="value.name">
@ -12,10 +38,10 @@
</div>
<div *ngIf="addShowMore && hasMoreValues">
<a *ngIf="filterValuesNum > 0 " class="uk-text-small view-more-less-link uk-margin-small-top" [ngClass]="((isDisabled)?'uk-disabled uk-link-muted ':'')" (click)="toggle($event)">
<span *ngIf="!filter.isOpen">View all</span>
<span *ngIf="filter.isOpen">View less </span>
<span *ngIf="!filter.isViewAllOpen">View all</span>
<span *ngIf="filter.isViewAllOpen">View less </span>
</a>
<div *ngIf="filter.isOpen" class="uk-text-small uk-margin-small-top uk-margin-small-bottom">
<div *ngIf="filter.isViewAllOpen" class="uk-text-small uk-margin-small-top uk-margin-small-bottom">
<div *ngIf="filter.countAllValues == -1">
<loading class="uk-height-small uk-display-block" size="medium"></loading>
</div>
@ -49,6 +75,67 @@
</div>
</div>
</div>
<!--<ul *ngIf="filter.values.length >0" class="uk-accordion">-->
<!-- <li (click)="filter.isOpen = !filter.isOpen" [class]="filter.isOpen ? 'uk-open' : ''">-->
<!-- <a class="uk-accordion-title">-->
<!-- {{filter.isOpen}}-->
<!-- <span>{{_formatTitle(filter.title, filter.values.length)}}</span>-->
<!-- <span *ngIf="filter.countSelectedValues>0" class="uk-margin-left">{{filter.countSelectedValues}}</span>-->
<!-- </a>-->
<!-- <div *ngIf="filter.isOpen" class="uk-accordion-content">-->
<!-- <div *ngFor="let value of getSelectedAndTopValues(filter, filterValuesNum)"-->
<!-- class="uk-animation-fade uk-text-small uk-margin-small-bottom">-->
<!-- <div [title]="value.name">-->
<!-- <ng-container *ngTemplateOutlet="input_label_wrapper; context: {filter: filter, value: value}"></ng-container>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div *ngIf="addShowMore && hasMoreValues">-->
<!-- <a *ngIf="filterValuesNum > 0 " class="uk-text-small view-more-less-link uk-margin-small-top" [ngClass]="((isDisabled)?'uk-disabled uk-link-muted ':'')" (click)="toggle($event)">-->
<!-- <span *ngIf="!filter.isViewAllOpen">View all</span>-->
<!-- <span *ngIf="filter.isViewAllOpen">View less </span>-->
<!-- </a>-->
<!-- <div *ngIf="filter.isViewAllOpen" class="uk-text-small uk-margin-small-top uk-margin-small-bottom">-->
<!-- <div *ngIf="filter.countAllValues == -1">-->
<!-- <loading class="uk-height-small uk-display-block" size="medium"></loading>-->
<!-- </div>-->
<!-- <div *ngIf="filter.countAllValues == 0">-->
<!-- <span class="uk-text-warning">An error occured. </span><span><a class="uk-button-link" (click)="toggleWithoutUpdate()">Please try again</a>.</span>-->
<!-- </div>-->
<!-- <ng-container *ngIf="(!filter.countAllValues && filter.countAllValues != 0) || filter.countAllValues > 0">-->
<!-- <div class="uk-margin-small-left">-->
<!-- <div class="uk-text-meta">Top 100 values are shown in the filters</div>-->
<!-- <div class="uk-flex uk-flex-bottom uk-margin-top">-->
<!-- <div input class="uk-width-1-2@m uk-margin-right" [placeholder]="{label: 'Search', static: true}" inputClass="inner small" [(value)]="keyword" (valueChange)="initMatching()"></div>-->
<!-- <div *ngIf="showResultCount === true" input type="select" class="uk-width-expand" placeholder="Sort by"-->
<!-- inputClass="border-bottom" [(value)]="sortBy" [options]="sortByOptions" (valueChange)="sort()"></div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="uk-overflow-auto uk-height-max-small uk-margin-small-left uk-margin-small-right uk-margin-top">-->
<!-- <ng-container *ngFor="let value of this.sortedValues">-->
<!-- <div *ngIf="filterKeywords(value.name)" title="{{value.name}}"-->
<!-- class="uk-animation-fade uk-text-small">-->
<!-- <ng-container *ngTemplateOutlet="input_label_wrapper; context: {filter: filter, value: value}"></ng-container>-->
<!-- </div>-->
<!-- </ng-container>-->
<!-- <ng-container *ngIf="!hasMatch">-->
<!-- <div class="uk-padding-small uk-text-meta">-->
<!-- No filters available with that term-->
<!-- </div>-->
<!-- </ng-container>-->
<!-- </div>-->
<!-- </ng-container>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </li>-->
<!--</ul>-->
<ng-template #input_label let-filter="filter" let-value="value">
<span *ngIf="filter.filterType == 'checkbox' || filter.filterType == 'radio'" class="uk-flex uk-flex-middle"
[class.uk-disabled]="isDisabled || (showResultCount && value.number === 0)">
@ -65,7 +152,7 @@
{{value.name=='true'?'Yes':'No'}}
</span>
<ng-template #noboolean>
{{_formatName(value)}}
<span class="uk-text-capitalize">{{_formatName(value)}} </span>
</ng-template>
<span *ngIf="showResultCount">({{value.number|number}})</span>

View File

@ -43,9 +43,10 @@ export class SearchFilterComponent implements OnInit, OnChanges {
@Input() actionRoute: boolean = false;
@Input() quickFilter: { filter: Filter, selected: boolean, filterId: string, value: string };
sub;
@Input() isOpen: boolean = false;
// @Input() isViewAllOpen: boolean = false;
sortedValues;
hasMatch: boolean = false;
@Input() grouped: boolean = false;
constructor(private _router: Router,
private route: ActivatedRoute,
@ -60,11 +61,11 @@ export class SearchFilterComponent implements OnInit, OnChanges {
ngOnInit() {
if(this.filterValuesNum == 0){
this.filter.isOpen = true;
this.filter.isViewAllOpen = true;
this.sortBy = "num";
}
// else{
// this.filter.isOpen = false;
// this.filter.isViewAllOpen = false;
// }
this.sub = this.route.queryParams.subscribe(params => {
this.queryParams = Object.assign({}, params);
@ -90,7 +91,7 @@ export class SearchFilterComponent implements OnInit, OnChanges {
// && value.name.toLowerCase() != "null"
// );
if (this.filter.filterType == "radio") {
if (this.filter.filterType == "radio" || this.filter.filterType == "triplet") {
this.filter.radioValue = "";
this.filter.values.forEach(value => {
if (value.selected) {
@ -165,6 +166,20 @@ export class SearchFilterComponent implements OnInit, OnChanges {
});
}
tripletFilterChange() {
if(this.filter.radioValue == "") {
this.clearFilter();
} else {
this.filter.countSelectedValues = 1;
this.filter.values.forEach(value => {
value.selected = (value.id == this.filter.radioValue);
});
this.onFilterChange.emit({
value: this.filter
});
}
}
clearFilter() {
for (var i = 0; i < this.filter.values.length; i++) {
this.filter.values[i].selected = false;
@ -274,7 +289,7 @@ export class SearchFilterComponent implements OnInit, OnChanges {
}
toggle(event) {
this.filter.isOpen = !this.filter.isOpen;
this.filter.isViewAllOpen = !this.filter.isViewAllOpen;
event.stopPropagation();
this.toggleWithoutUpdate();
}
@ -283,7 +298,7 @@ export class SearchFilterComponent implements OnInit, OnChanges {
if(this.filter.countAllValues == 0) {
this.filter.countAllValues = -1; // if request failed, try again automatically if toggled again
}
if(this.filter.isOpen && this.filter.countAllValues < 0) {
if(this.filter.isViewAllOpen && this.filter.countAllValues < 0) {
this.onFilterToggle.emit(this.filter);
}
}

View File

@ -9,11 +9,12 @@ import {RouterModule} from "@angular/router";
import {InputModule} from '../../sharedComponents/input/input.module';
import {IconsModule} from "../../utils/icons/icons.module";
import {LoadingModule} from "../../utils/loading/loading.module";
import {MatButtonToggleModule} from "@angular/material/button-toggle";
@NgModule({
imports: [
CommonModule, FormsModule, ModalModule, RouterModule,
InputModule, IconsModule, LoadingModule
InputModule, IconsModule, LoadingModule, MatButtonToggleModule
],
declarations: [
SearchFilterComponent, SearchFilterModalComponent

View File

@ -12,7 +12,8 @@ export class Filter{
public radioValue?: string = "";
// public uniqueValueIdSelected: string;
public countAllValues?: number = -1; // -1: not yet requested, 0: request failed, >0 OK
public isOpen?: boolean = false;
// public isOpen?: boolean = false;
public isViewAllOpen?: boolean = false;
public countUnfilteredValues?: number = 0;
}

View File

@ -27,18 +27,20 @@ export class SearchCustomFilter{
valueName:string; // Greece
isHiddenFilter:boolean;
selected:boolean;
promptToAddFilter:boolean;
constructor( fieldName:string, queryFieldName:string, valueId:string, valueName:string ){
showFilterMessage:boolean;
customQuery:string;
constructor( fieldName:string, queryFieldName:string, valueId:string, valueName:string, showFilterMessage:boolean = true, customQuery = null ){
if(valueId == "test" && properties.environment == "development"){
valueId = "covid-19";
}
this.showFilterMessage = showFilterMessage;
this.isHiddenFilter = true;
this.fieldName = fieldName;
this.queryFieldName = queryFieldName;
this.valueId = valueId;
this.valueName = valueName;
this.selected = null;
this.promptToAddFilter = false;
this.customQuery = customQuery;
}
public getParameters(params={}){

View File

@ -6,6 +6,7 @@ import {StringUtils} from '../utils/string-utils.class';
import{EnvProperties} from '../utils/properties/env-properties';
import {map} from "rxjs/operators";
import {ParsingFunctions} from "../landingPages/landing-utils/parsingFunctions.class";
import {properties} from '../../../environments/environment';
@Injectable()
export class SearchDataprovidersService {
@ -116,7 +117,7 @@ export class SearchDataprovidersService {
} else {
result['englishname'] = "";
}
result['originalId'] = resData.originalId;
//result['title'].url = OpenaireProperties.getsearchLinkToDataProvider();
//result['title'].url += Array.isArray(data) ? data[i]['result']['header']['dri:objIdentifier'] : data['result']['header']['dri:objIdentifier'];
result['id'] = Array.isArray(data) ? data[i]['result']['header']['dri:objIdentifier'] : data['result']['header']['dri:objIdentifier'];
@ -289,4 +290,11 @@ export class SearchDataprovidersService {
return this.http.get((properties.useCache)? (properties.cacheUrl+encodeURIComponent(url)): url)
.pipe(map(res => res['meta']['total']));
}
searchDataproviderById(id: string):any {
let url = properties.searchAPIURLLAst + "datasources/" + id + '?format=json';
return this.http.get((properties.useCache)? (properties.cacheUrl+encodeURIComponent(url)): url)
.pipe(map(res => this.parseResults(res)));
}
}

View File

@ -427,6 +427,20 @@ export class SearchResearchResultsService {
result.hostedBy_collectedFrom, result.publisher,
result['journal']?result['journal'].journal:null, result.identifiers);
if(resData.hasOwnProperty("publiclyfunded") && resData.publiclyfunded) {
result.publiclyFunded = resData.publiclyfunded;
}
if((resData.hasOwnProperty("isgreen") && resData.isgreen)
|| (resData.hasOwnProperty("openaccesscolor") && resData.openaccesscolor)
|| (resData.hasOwnProperty("isindiamondjournal") && resData.isindiamondjournal)) {
result.oaRoutes = {
"green": resData.isgreen,
"oaColor": resData.openaccesscolor,
"isInDiamondJournal":resData.isindiamondjournal
};
}
results.push(result);
}
return results;
@ -598,4 +612,9 @@ export class SearchResearchResultsService {
return this.http.get((properties.useCache) ? (properties.cacheUrl + encodeURIComponent(url)) : url)
.pipe(map(res => res['meta']['total']));
}
fetchByDOIs(DOIs:string[],query:string): any {
let url = properties.searchAPIURLLAst + "/researchProducts/byDoi?type=publications" + (query?query:"");
return this.http.post(url, {doiArray: DOIs})
.pipe(map(res => [res['meta'].total, this.parseResults("result", res['results'], properties), RefineResultsUtils.parse(res['refineResults'], null, "publication")]));
}
}

View File

@ -7,13 +7,18 @@ import {properties} from "../../../environments/environment";
import {StringUtils} from "../utils/string-utils.class";
import {CustomOptions} from "./servicesUtils/customOptions.class";
import {AdvancedAsyncSubject} from "../utils/AdvancedAsyncSubject";
import {isArray} from "rxjs/internal-compatibility";
@Injectable({
providedIn: 'root'
})
export class UserManagementService {
private readonly getUserInfoSubject: AdvancedAsyncSubject<User> = new AdvancedAsyncSubject<User>();
private static LOGIN = 'openid_connect_login';
private static LOGOUT: string = 'openid_logout';
private static USERINFO: string = 'userInfo';
public fixRedirectURL: string = null;
public allowDoubleRedirectToFixAndCurrentPage: boolean = false;
private redirectUrl: string = null;
private subscription;
private readonly routerSubscription;
@ -31,6 +36,10 @@ export class UserManagementService {
}
}
public static userInfoUrl(index = 0): string {
return (isArray(properties.loginServiceURL)?properties.loginServiceURL[index]:properties.loginServiceURL) + UserManagementService.USERINFO;
}
public get user(): User {
return this.getUserInfoSubject.getValue();
}
@ -39,10 +48,15 @@ export class UserManagementService {
return this.getUserInfoSubject.asObservable();
}
public updateUserInfo(resolve: Function = null) {
this.subscription = this.http.get<User>(properties.userInfoUrl, CustomOptions.registryOptions()).pipe(map(userInfo => {
public getUserInfoAt(index = 0): Observable<User> {
return this.http.get<User>(UserManagementService.userInfoUrl(index), CustomOptions.registryOptions()).pipe(map(userInfo => {
return new User(userInfo);
})).subscribe(user => {
}))
}
public updateUserInfo(resolve: Function = null) {
this.subscription = this.getUserInfoAt().subscribe(user => {
this.getUserInfoSubject.next(user);
if (resolve) {
resolve();
@ -55,8 +69,9 @@ export class UserManagementService {
});
}
public setRedirectUrl(url: string = null) {
if (url) {
public setRedirectUrl(redirectTo: string | string [] = null) {
let urls = Array.isArray(redirectTo)?redirectTo:(redirectTo?[redirectTo]:[])
for(let url of urls) {
let parts = url.split('?');
let path = properties.baseLink + parts[0];
let params = null;
@ -74,24 +89,47 @@ export class UserManagementService {
Session.setReloadUrl(location.protocol + "//" + location.host, path, params, fragment);
}
this.redirectUrl = StringUtils.URIEncode(location.protocol + "//" + location.host + this.fixRedirectURL);
} else {
}
if(urls.length == 0){
this.redirectUrl = StringUtils.URIEncode(location.href);
Session.setReloadUrl(location.protocol + "//" + location.host, location.pathname, location.search, location.hash);
}
}
public login() {
if (this.fixRedirectURL) {
this.setRedirectUrl(this.fixRedirectURL);
public login(redirect: string = null) {
Session.clearReloadUrl();
if(redirect) {
this.redirectUrl = redirect;
} else if (this.fixRedirectURL) {
this.setRedirectUrl(this.allowDoubleRedirectToFixAndCurrentPage?[this.fixRedirectURL,location.href.split(location.host)[1]]:this.fixRedirectURL);
} else {
this.setRedirectUrl();
}
window.location.href = properties.loginUrl + "?redirect=" + this.redirectUrl;
let loginURL: string | string[] = isArray(properties.loginServiceURL)?properties.loginServiceURL.map(url => url + UserManagementService.LOGIN):(properties.loginServiceURL + UserManagementService.LOGIN);
window.location.href = this.setURL(loginURL) + this.redirectUrl;
}
public logout() {
Session.clearReloadUrl();
this.setRedirectUrl();
Session.removeUser();
window.location.href = properties.logoutUrl + "?redirect=" + this.redirectUrl;
let logoutURL: string | string[] = isArray(properties.loginServiceURL)?properties.loginServiceURL.map(url => url + UserManagementService.LOGOUT):(properties.loginServiceURL + UserManagementService.LOGOUT);
window.location.href = this.setURL(logoutURL) + this.redirectUrl;
}
setURL(url: string | string[]) {
if(isArray(url)) {
let redirectURL = '';
url.forEach((url, index) => {
if(index === 0) {
redirectURL = url + "?redirect=";
} else {
redirectURL += encodeURIComponent(url + "?redirect=");
}
});
return redirectURL;
} else {
return url + "?redirect=";
}
}
}

View File

@ -1,9 +1,10 @@
import {Injectable} from '@angular/core';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';
import {properties} from '../../../environments/environment';
import {CustomOptions} from './servicesUtils/customOptions.class';
import {map} from 'rxjs/operators';
import {Role} from "../login/utils/helper.class";
@Injectable({
providedIn: 'root'
@ -14,7 +15,7 @@ export class UserRegistryService {
}
public createRole(type: string, id: string): Observable<any[]> {
return this.http.post<any>(properties.registryUrl + 'create/' + type + '/' + id, null,
return this.http.post<any>(properties.registryUrl + 'create/' + Role.GROUP + type + '/' + id, null,
CustomOptions.registryOptions()).pipe(map((response: any) => response.response));
}
@ -35,12 +36,12 @@ export class UserRegistryService {
public remove(type: string, id: string, email: string, role: "member" | "manager" = "manager"): Observable<any> {
return this.http.delete<any>(properties.registryUrl +
type + '/' + id + '/' + role + '/' + encodeURIComponent(email), CustomOptions.registryOptions());
Role.GROUP + type + '/' + id + '/' + role + '/' + encodeURIComponent(email), CustomOptions.registryOptions());
}
public invite(type: string, id: string, details: any, role: "member" | "manager" = "manager"): Observable<any[]> {
return this.http.post<any>(properties.registryUrl + 'invite/' +
type + '/' + id + '/' + role, details,
Role.GROUP + type + '/' + id + '/' + role, details,
CustomOptions.registryOptions()).pipe(map((response: any) => response.response));
}
@ -57,9 +58,9 @@ export class UserRegistryService {
return this.http.delete<any>(properties.registryUrl + 'verification/' + id, CustomOptions.registryOptions());
}
public getActive(type: string, id: string, role: "member" | "manager" = "manager"): Observable<any[]> {
let url = properties.registryUrl + type + '/' + id + "/" + role + 's';
return this.http.get<any>((properties.useCache) ? (properties.cacheUrl + encodeURIComponent(url)) : url,
public getActive(type: string, id: string, role: "member" | "manager" = "manager", admin = false): Observable<any[]> {
let url = properties.registryUrl + Role.GROUP + type + '/' + id + "/" + role + 's';
return this.http.get<any>((properties.useCache && !admin) ? (properties.cacheUrl + encodeURIComponent(url)) : url,
CustomOptions.registryOptions()).pipe(map((response:any) => response.response), map(users => {
if(users.length > 0 && !users[0].email) {
return [];
@ -69,15 +70,15 @@ export class UserRegistryService {
}));
}
public getPending(type: string, id: string, role: "member" | "manager" = "manager"): Observable<any[]> {
let url = properties.registryUrl + 'invite/' +type + '/' +id + "/" + role + 's/';
return this.http.get<any>((properties.useCache) ? (properties.cacheUrl + encodeURIComponent(url)) : url,
public getPending(type: string, id: string, role: "member" | "manager" = "manager", admin = false): Observable<any[]> {
let url = properties.registryUrl + 'invite/' + Role.GROUP + type + '/' +id + "/" + role + 's/';
return this.http.get<any>((properties.useCache && !admin) ? (properties.cacheUrl + encodeURIComponent(url)) : url,
CustomOptions.registryOptions()).pipe(map((response: any) => response.response));
}
public cancelInvitation(type: string, id: string, email: string, role: "member" | "manager" = "manager"): Observable<any> {
return this.http.delete<any>(properties.registryUrl + 'invite/' +
type + '/' + id + '/' + role + '/' + encodeURIComponent(email),
Role.GROUP + type + '/' + id + '/' + role + '/' + encodeURIComponent(email),
CustomOptions.registryOptions());
}
}

View File

@ -0,0 +1,70 @@
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs";
import {EnvProperties} from "../utils/properties/env-properties";
import {CustomOptions} from "./servicesUtils/customOptions.class";
import {AdvancedAsyncSubject} from "../utils/AdvancedAsyncSubject";
import {properties} from "../../../environments/environment";
import {map} from "rxjs/operators";
export class UserProfile{
id:string;
aaiId:string;
consent:boolean = false;
constructor(consent:boolean) {
this.consent = consent;
}
}
@Injectable({
providedIn: "root"
})
export class UserProfileService {
private subscription;
private readonly userProfileSubject: AdvancedAsyncSubject<UserProfile> = new AdvancedAsyncSubject<UserProfile>();
constructor(private http: HttpClient) {
}
clearSubscriptions() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
public get user(): UserProfile {
return this.userProfileSubject.getValue();
}
public getUserProfile(): Observable<UserProfile> {
return this.userProfileSubject.asObservable();
}
public setUserProfile(userProfile:UserProfile) {
this.userProfileSubject.next(userProfile?userProfile:new UserProfile(false));
}
public initUserProfile(resolve: Function = null) {
this.subscription = this.http.get<UserProfile>(properties.monitorServiceAPIURL + 'user', CustomOptions.registryOptions()).pipe(map(userProfile => {
return userProfile;
})).subscribe(user => {
this.userProfileSubject.next(user);
if (resolve) {
resolve();
}
}, error => {
this.userProfileSubject.next(new UserProfile(false));
if (resolve) {
resolve();
}
});
}
saveConsentInUserProfile(properties:EnvProperties): Observable<UserProfile> {
return this.http.post<UserProfile>(properties.monitorServiceAPIURL + 'user/save', new UserProfile(true), CustomOptions.registryOptions(),);
}
undoConsentInUserProfile(properties:EnvProperties): Observable<UserProfile> {
return this.http.post<UserProfile>(properties.monitorServiceAPIURL + 'user/save', new UserProfile(false), CustomOptions.registryOptions(),);
}
}

View File

@ -0,0 +1,56 @@
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {BehaviorSubject, from, Observable, Subscriber} from "rxjs";
import {SearchCustomFilter} from "../searchPages/searchUtils/searchUtils.class";
@Injectable({
providedIn: "root"
})
export class CustomFilterService {
private customFilterSubject: BehaviorSubject<SearchCustomFilter[]> = null;
private promise: Promise<void>;
private sub;
constructor(private http: HttpClient) {
this.customFilterSubject = new BehaviorSubject<SearchCustomFilter[]>(null);
}
ngOnDestroy() {
this.clearSubscriptions();
}
clearSubscriptions() {
if (this.sub instanceof Subscriber) {
this.sub.unsubscribe();
}
}
async getCustomFilterAsync() {
if (this.promise) {
await this.promise;
this.promise = null;
}
this.clearSubscriptions();
return this.customFilterSubject.getValue();
}
getCustomFilterAsObservable(): Observable<SearchCustomFilter[]> {
return this.customFilterSubject.asObservable();
}
setCustomFilter(customFilters: SearchCustomFilter[]) {
this.customFilterSubject.next(customFilters);
}
/*
static getDefaultFilters(){
let defaultFilters = new Map<string, SearchCustomFilter[]>();
defaultFilters.set("result",[new SearchCustomFilter("National", "country", "IE", "Irish National Monitor")]);
defaultFilters.set("project",[new SearchCustomFilter("National", "country", "IE", "Irish National Monitor")]);
defaultFilters.set("organization",[new SearchCustomFilter("National", "country", "IE", "Irish National Monitor")]);
defaultFilters.set("datasource",[new SearchCustomFilter("National", "country", "IE", "Irish National Monitor")]);
return defaultFilters;
}*/
}

View File

@ -2,7 +2,9 @@ import { Injectable, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { Optional, RendererFactory2, ViewEncapsulation } from '@angular/core';
@Injectable()
@Injectable({
providedIn: 'root'
})
export class SEOService {
constructor( @Inject(DOCUMENT) private doc,
private rendererFactory: RendererFactory2,

View File

@ -0,0 +1,112 @@
import {Directive, OnDestroy} from "@angular/core";
import {BehaviorSubject, Subscriber} from "rxjs";
import {EnvProperties} from "../../utils/properties/env-properties";
import {properties} from "src/environments/environment";
import {PiwikService} from "../../utils/piwik/piwik.service";
import {Meta, Title} from "@angular/platform-browser";
import {SEOService} from "../SEO/SEO.service";
import {ActivatedRoute, Data, NavigationEnd, Params, Router} from "@angular/router";
import {StringUtils} from "../../utils/string-utils.class";
import {RouterHelper} from "../../utils/routerHelper.class";
@Directive()
export abstract class BaseComponent implements OnDestroy {
public properties: EnvProperties = properties;
/** Router params */
protected paramsResolved: boolean = false;
protected params: BehaviorSubject<Params>;
protected data: BehaviorSubject<Data>;
protected subscriptions: any[] = [];
/** Metadata */
public title: string;
public description: string;
public url: string;
protected _route: ActivatedRoute;
protected _piwikService: PiwikService;
protected _meta: Meta;
protected seoService: SEOService;
protected _title: Title;
protected _router: Router;
public routerHelper: RouterHelper = new RouterHelper();
protected constructor() {
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => {
if (subscription instanceof Subscriber) {
subscription.unsubscribe()
} else if (subscription instanceof Function) {
subscription();
} else if (typeof IntersectionObserver !== 'undefined' && subscription instanceof IntersectionObserver) {
subscription.disconnect();
} else if (typeof ResizeObserver !== 'undefined' && subscription instanceof ResizeObserver) {
subscription.disconnect();
}
});
}
/**
* Initialize router params and data (should be called in the constructor of a component with router-outlet)
* */
initRouterParams(route: ActivatedRoute = null, navigationChange: ((event: NavigationEnd) => void) = null) {
if (route) {
this.params = new BehaviorSubject<Params>(null);
this.data = new BehaviorSubject<Data>(null);
this.subscriptions.push(this._router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
if(navigationChange) {
navigationChange(event);
}
let r = route;
while (r.firstChild) {
r = r.firstChild;
}
let data = r.snapshot.data;
let params = r.snapshot.params;
let current = r.snapshot;
while (current.parent) {
data = {...current.parent.data, ...data};
params = {...current.parent.params, ...params};
current = current.parent;
}
r.snapshot.data = data;
r.snapshot.params = params;
this.paramsResolved = true;
this.params.next(r.snapshot.params);
this.data.next(r.snapshot.data);
}
}));
}
}
public setMetadata() {
if (this._title && this.title) {
this.title = (this._route?.snapshot.data?.title ? this._route.snapshot.data?.title : '') + this.title;
this._title.setTitle(this.title);
}
if (this._router) {
this.url = properties.domain + properties.baseLink + this._router.url;
if (this.seoService) {
this.seoService.createLinkForCanonicalURL(this.url, false);
}
if (this._meta) {
this._meta.updateTag({content: this.url}, "property='og:url'");
this._meta.updateTag({content: this.description}, "name='description'");
this._meta.updateTag({content: this.description}, "property='og:description'");
this._meta.updateTag({content: this.title}, "property='og:title'");
}
}
this.trackView();
}
public trackView() {
if (this._piwikService) {
this.subscriptions.push(this._piwikService.trackView(properties, this.title).subscribe());
}
}
public quote(str: string) {
return StringUtils.quote(str);
}
}

View File

@ -66,7 +66,7 @@ export type CookieLawTarget = '_blank' | '_self';
})
export class CookieLawComponent implements OnInit {
public cookieLawSeen: boolean;
@Input() cookieName = "cookieLawSeen";
@Input('learnMore')
get learnMore() { return this._learnMore; }
set learnMore(value: string) {
@ -109,10 +109,11 @@ export class CookieLawComponent implements OnInit {
this.isSeenEvt = new EventEmitter<boolean>();
this.animation = 'topIn';
this._position = 'bottom';
this.cookieLawSeen = this._service.seen();
}
ngOnInit(): void {
this.cookieLawSeen = this._service.seen(this.cookieName);
if (typeof document !== 'undefined') {
this.animation = this.position === 'bottom' ? 'bottomIn' : 'topIn';
@ -142,7 +143,7 @@ export class CookieLawComponent implements OnInit {
evt.preventDefault();
}
this._service.storeCookie();
this._service.storeCookie(this.cookieName);
this.animation = this.position === 'top' ? 'topOut' : 'bottomOut';
}
}

View File

@ -12,12 +12,12 @@ import { Injectable } from '@angular/core';
export class CookieLawService {
seen(): boolean {
return this.cookieExists('cookieLawSeen');
seen(cookieName): boolean {
return this.cookieExists(cookieName);
}
storeCookie(): void {
return this.setCookie('cookieLawSeen');
storeCookie(cookieName): void {
return this.setCookie(cookieName);
}
/**

View File

@ -65,8 +65,6 @@ export interface ControlConfiguration {
declare var UIkit;
/**
* WARNING! dashboard-input selector is @deprecated, use input instead
*
* Autocomplete soft allows values that are not listed in options list. In order to work as expected
* avoid providing options with different label and value.
*
@ -478,7 +476,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
ngOnChanges(changes: SimpleChanges) {
if (this.formControl) {
if (changes.value) {
if (changes.value && changes.value.currentValue !== changes.value.previousValue) {
this.formControl.setValue(this.value);
}
if (changes.validators) {
@ -826,6 +824,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
resetValue(event: any) {
event.stopPropagation();
console.log(1)
this.formControl.setValue('');
this.focus(true, event);
}

View File

@ -12,9 +12,7 @@
</div>
<div *ngIf="!onlyTop || userMenu" class="uk-navbar-right" [class.uk-light]='activeHeader.darkBg'>
<ng-container *ngIf="userMenu">
<user-mini [user]="user" mobileView=true
[userMenuItems]=userMenuItems [logInUrl]=properties.loginUrl [notificationConfiguration]="notificationConfiguration"
[logOutUrl]=properties.logoutUrl [cookieDomain]=properties.cookieDomain></user-mini>
<user-mini [user]="user" mobileView=true [userMenuItems]=userMenuItems [notificationConfiguration]="notificationConfiguration"></user-mini>
</ng-container>
</div>
</nav>
@ -151,11 +149,15 @@
<div id="main-menu" class="uk-visible@m">
<div *ngIf="activeHeader" [class.uk-light]='activeHeader.darkBg'>
<div class="uk-navbar-container" uk-sticky>
<div *ngIf="(properties.environment =='beta' || properties.environment =='development') && showLogo && activeHeader.badge">
<img class="uk-position-top-left"
<a *ngIf="(properties.environment =='beta' || properties.environment =='development' || activeHeader.environmentBadge) && showLogo && activeHeader.badge"
[routerLink]="activeHeader.environmentBadge?.routerLink?activeHeader.environmentBadge.routerLink:null" target="_blank">
<img *ngIf="!activeHeader.environmentBadge" class="uk-position-top-left"
[src]="'assets/common-assets/'+(properties.environment =='beta'?'beta_flag.svg':'prototype_flag.svg')"
alt="BETA" style="height: 65px; width: 65px; z-index: 1000">
</div>
<img *ngIf="activeHeader.environmentBadge" class="uk-position-top-left"
[src]="activeHeader.environmentBadge.asset" [alt]="properties.environment"
style="height: 75px; width: 75px; z-index: 1000">
</a>
<div class="uk-container uk-container-expand">
<nav class="uk-navbar" uk-navbar="delay-hide: 400">
<ng-container *ngIf="!onlyTop">
@ -192,9 +194,7 @@
</div>
</div>
<user-mini *ngIf="!searchMode && userMenu" [user]="user"
[userMenuItems]=userMenuItems [logInUrl]=properties.loginUrl [logOutUrl]=properties.logoutUrl
[cookieDomain]=properties.cookieDomain></user-mini>
<user-mini *ngIf="!searchMode && userMenu" [user]="user" [userMenuItems]=userMenuItems></user-mini>
<div class="uk-visible@m">
<ng-content select="[extra-m]"></ng-content>
</div>

View File

@ -24,6 +24,12 @@ import {RouterHelper} from "../utils/routerHelper.class";
declare var UIkit;
export interface Badge {
asset: string,
routerLink?: string,
external?: boolean
}
export interface Header {
route?: string,
url?: string,
@ -33,6 +39,7 @@ export interface Header {
logoInfo?: string,
position: 'left' | 'center' | 'right',
badge: boolean,
environmentBadge?: Badge,
darkBg?: boolean,
menuPosition?: 'center' | 'right',
replaceHeader?: Header;

View File

@ -1,4 +1,5 @@
import {Component, Input} from "@angular/core";
import {ActivatedRoute} from "@angular/router";
@Component({
selector: 'slider-tab',
@ -22,4 +23,6 @@ export class SliderTabComponent {
@Input()
public customClass: string = '';
@Input() tabTemplate: any;
@Input()
public relativeTo: ActivatedRoute = null;
}

View File

@ -43,7 +43,7 @@ declare var UIkit;
</ng-container>
<ng-container *ngIf="type === 'dynamic'">
<li *ngFor="let tab of leftTabs; let i=index;" [class.uk-active]="tab.active" [style.max-width]="(position === 'horizontal')?'50%':null">
<a [routerLink]="tab.routerLink" [queryParams]="tab.queryParams" [ngClass]="tab.customClass"
<a [routerLink]="tab.routerLink" [queryParams]="tab.queryParams" [ngClass]="tab.customClass" [relativeTo]="tab.relativeTo"
(click)="showActive(i)"
class="uk-text-capitalize uk-text-truncate uk-display-block">
<span *ngIf="tab.title">{{tab.title}}</span>
@ -52,7 +52,7 @@ declare var UIkit;
</li>
<li *ngFor="let tab of rightTabs; let i=index;" [style.max-width]="(position === 'horizontal')?'50%':null" [class.uk-active]="tab.active"
[ngClass]="i === 0?'uk-flex-1 uk-flex uk-flex-right':''">
<a [routerLink]="tab.routerLink" [queryParams]="tab.queryParams" [ngClass]="tab.customClass"
<a [routerLink]="tab.routerLink" [queryParams]="tab.queryParams" [ngClass]="tab.customClass" [relativeTo]="tab.relativeTo"
(click)="showActive(i)"
class="uk-text-capitalize uk-text-truncate uk-display-block">
<span *ngIf="tab.title">{{tab.title}}</span>

View File

@ -1,4 +1,13 @@
import {Component, Inject, Input, PLATFORM_ID, ViewChild} from '@angular/core';
import {
AfterContentInit,
AfterViewInit,
ChangeDetectorRef,
Component,
Inject,
Input,
PLATFORM_ID,
ViewChild
} from '@angular/core';
import {ActivatedRoute} from "@angular/router";
import {RouterHelper} from "../routerHelper.class";
import {EnvProperties} from '../properties/env-properties';
@ -11,89 +20,89 @@ import {properties} from "../../../../environments/environment";
selector: 'showAuthors',
template: `
<ng-template #author_template let-author="author" let-i="i" let-italic="italic">
<span *ngIf="isSticky || (!author.orcid && !author.orcid_pending) || !isBrowser" style="margin-right: 5px;"
[class.uk-text-italic]="italic">
{{author.fullName + ";"}}
</span>
<ng-container *ngIf="!isSticky && (author.orcid || author.orcid_pending) && isBrowser">
<a #toggle class="uk-flex-inline uk-flex-middle uk-link-text">
<img *ngIf="author.orcid" src="assets/common-assets/common/ORCIDiD_icon16x16.png" alt="orcid"
loading="lazy" style="width:16px; height:16px; margin-right: 3px;">
<img *ngIf="!author.orcid && author.orcid_pending"
src="assets/common-assets/common/ORCIDiD_iconbw16x16.png" alt="orcid bw" loading="lazy"
style="margin-right: 3px;">
<span style="margin-right: 5px;" [class.uk-text-italic]="italic">
{{author.fullName + ";"}}
</span>
</a>
<div *ngIf="!isMobile" class="default-dropdown uk-margin-remove-top uk-dropdown orcid-dropdown" uk-dropdown="mode:click; offset: 4; container: true" style="min-width: 465px !important;">
<ng-container *ngTemplateOutlet="dropdown"></ng-container>
</div>
<mobile-dropdown *ngIf="isMobile" [toggle]="toggle">
<div class="orcid-dropdown">
<ng-container *ngTemplateOutlet="dropdown"></ng-container>
</div>
</mobile-dropdown>
<ng-template #dropdown>
<div class="uk-padding-small">
<h6 class="uk-margin-remove">{{author.fullName}}</h6>
<div>
<div class="uk-text-meta uk-margin-bottom">ORCID</div>
<span *ngIf="isSticky || (!author.orcid && !author.orcid_pending) || !isBrowser" style="margin-right: 5px;"
[class.uk-text-italic]="italic">
{{author.fullName + ";"}}
</span>
<ng-container *ngIf="!isSticky && (author.orcid || author.orcid_pending) && isBrowser">
<a #toggle class="uk-flex-inline uk-flex-middle uk-link-text">
<img *ngIf="author.orcid" src="assets/common-assets/common/ORCIDiD_icon16x16.png" alt="orcid"
loading="lazy" style="width:16px; height:16px; margin-right: 3px;">
<img *ngIf="!author.orcid && author.orcid_pending"
src="assets/common-assets/common/ORCIDiD_iconbw16x16.png" alt="orcid bw" loading="lazy"
style="margin-right: 3px;">
<span style="margin-right: 5px;" [class.uk-text-italic]="italic">
{{author.fullName + ";"}}
</span>
</a>
<div *ngIf="!isMobile" class="default-dropdown uk-margin-remove-top uk-dropdown orcid-dropdown"
uk-dropdown="mode:click; offset: 4;" style="min-width: 465px !important;" [attr.container]="isModal?'#modal-container':true">
<ng-container *ngTemplateOutlet="dropdown"></ng-container>
</div>
<mobile-dropdown *ngIf="isMobile" [toggle]="toggle">
<div class="orcid-dropdown">
<ng-container *ngTemplateOutlet="dropdown"></ng-container>
</div>
</mobile-dropdown>
<ng-template #dropdown>
<div class="uk-padding-small">
<h6 class="uk-margin-remove">{{author.fullName}}</h6>
<div>
<div class="uk-text-meta uk-margin-bottom">ORCID</div>
<div class="uk-text-meta uk-text-xsmall uk-margin-small-top uk-margin-small-bottom">
<img *ngIf="author.orcid" src="assets/common-assets/common/ORCIDiD_icon16x16.png" alt=""
loading="lazy">{{" "}}
<img *ngIf="!author.orcid && author.orcid_pending"
src="assets/common-assets/common/ORCIDiD_iconbw16x16.png" alt="" loading="lazy">{{" "}}
<div class="uk-text-meta uk-text-xsmall uk-margin-small-top uk-margin-small-bottom">
<img *ngIf="author.orcid" src="assets/common-assets/common/ORCIDiD_icon16x16.png" alt=""
loading="lazy">{{" "}}
<img *ngIf="!author.orcid && author.orcid_pending"
src="assets/common-assets/common/ORCIDiD_iconbw16x16.png" alt="" loading="lazy">{{" "}}
<i *ngIf="author.orcid">Harvested from ORCID Public Data File</i>
<i *ngIf="!author.orcid && author.orcid_pending">Derived by OpenAIRE algorithms or harvested
from 3d party repositories</i>
</div>
<div>
<div class="clipboard-wrapper uk-width-1-1 uk-flex uk-flex-middle uk-flex-center"
style="min-height: 43px; min-width: 280px;">
<input #element class="uk-padding-small uk-text-emphasis uk-width-expand uk-disabled"
[value]="properties.orcidURL+(author.orcid ? author.orcid : author.orcid_pending)"/>
<button class="uk-button uk-button-link uk-margin-small-right copy orcid_clipboard_btn" [ngClass]="'orcid_clipboard_btn_auhtor_'+i"
title="Copy to clipboard" (click)="copyToClipboard(element)">
<icon name="content_copy" visuallyHidden="Copy to clipboard" [flex]="true"></icon>
</button>
</div>
<div class="uk-text-center uk-margin-small-top">
<a class="uk-button uk-button-text custom-external"
title="Visit author in ORCID"
[href]="properties.orcidURL+(author.orcid ? author.orcid : author.orcid_pending)"
target="_blank">
VISIT AUTHOR IN ORCID
</a>
</div>
</div>
</div>
<hr>
<div class="uk-margin-top uk-text-bold uk-text-emphasis uk-text-center">
{{author.fullName}} in OpenAIRE
</div>
<div class="uk-text-center uk-margin-top uk-margin-large-left uk-margin-large-right">
<a *ngIf="properties.adminToolsPortalType !== 'eosc'"
class="uk-button uk-button-primary uk-text-bold uk-padding-remove-top uk-padding-remove-bottom"
(click)="onClick()"
[queryParams]="routerHelper.createQueryParams(['orcid','oc'],[(author['orcid'] ? author['orcid'] : author['orcid_pending']),'and'])"
routerLinkActive="router-link-active" [routerLink]="properties.searchLinkToAdvancedResults">
<span class="space">Search</span>
</a>
<a *ngIf="properties.adminToolsPortalType == 'eosc'"
class="uk-button uk-button-primary uk-text-bold uk-padding-remove-top uk-padding-remove-bottom uk-light"
[href]="'https://explore.openaire.eu'+properties.searchLinkToAdvancedResults+'?orcid='+(author['orcid'] ? author['orcid'] : author['orcid_pending'])+'&oc=and'"
target="_blank">
<span class="space custom-external">Search in OpenAIRE</span>
</a>
</div>
</div>
</ng-template>
</ng-container>
<i *ngIf="author.orcid">Harvested from ORCID Public Data File</i>
<i *ngIf="!author.orcid && author.orcid_pending">Derived by OpenAIRE algorithms or harvested
from 3d party repositories</i>
</div>
<div>
<div class="clipboard-wrapper uk-width-1-1 uk-flex uk-flex-middle uk-flex-center"
style="min-height: 43px; min-width: 280px;">
<input #element class="uk-padding-small uk-text-emphasis uk-width-expand uk-disabled"
[value]="properties.orcidURL+(author.orcid ? author.orcid : author.orcid_pending)"/>
<button class="uk-button uk-button-link uk-margin-small-right copy orcid_clipboard_btn" [ngClass]="'orcid_clipboard_btn_auhtor_'+i"
title="Copy to clipboard" (click)="copyToClipboard(element)">
<icon name="content_copy" visuallyHidden="Copy to clipboard" [flex]="true"></icon>
</button>
</div>
<div class="uk-text-center uk-margin-small-top">
<a class="uk-button uk-button-text custom-external"
title="Visit author in ORCID"
[href]="properties.orcidURL+(author.orcid ? author.orcid : author.orcid_pending)"
target="_blank">
VISIT AUTHOR IN ORCID
</a>
</div>
</div>
</div>
<hr>
<div class="uk-margin-top uk-text-bold uk-text-emphasis uk-text-center">
{{author.fullName}} in OpenAIRE
</div>
<div class="uk-text-center uk-margin-top uk-margin-large-left uk-margin-large-right">
<a *ngIf="properties.adminToolsPortalType !== 'eosc'"
class="uk-button uk-button-primary uk-text-bold uk-padding-remove-top uk-padding-remove-bottom"
(click)="onClick()"
[queryParams]="routerHelper.createQueryParams(['orcid','oc'],[(author['orcid'] ? author['orcid'] : author['orcid_pending']),'and'])"
routerLinkActive="router-link-active" [routerLink]="properties.searchLinkToAdvancedResults">
<span class="space">Search</span>
</a>
<a *ngIf="properties.adminToolsPortalType == 'eosc'"
class="uk-button uk-button-primary uk-text-bold uk-padding-remove-top uk-padding-remove-bottom uk-light"
[href]="'https://explore.openaire.eu'+properties.searchLinkToAdvancedResults+'?orcid='+(author['orcid'] ? author['orcid'] : author['orcid_pending'])+'&oc=and'"
target="_blank">
<span class="space custom-external">Search in OpenAIRE</span>
</a>
</div>
</div>
</ng-template>
</ng-container>
</ng-template>
<div *ngIf="authors"
@ -144,6 +153,7 @@ export class ShowAuthorsComponent {
@Input() authorsLimit: number = 7;
@Input() showAll: boolean = true;
@Input() modal: AlertModal;
@Input() isModal: boolean
@Input() viewAll: boolean = false;
@Input() showInline: boolean = false; // do not open modal for "view more" when this is true
public lessBtn: boolean = false;

View File

@ -94,6 +94,19 @@ export class Composer {
return email;
}
public static composeEmailforIrishMonitor(contactForm: any, admins: any): Email {
let email: Email = new Email();
email.subject = "National OA Monitor Ireland | " + contactForm.subject;
email.body = "<div style='font-size:" + this.noteBodySize + "'>"
+ "<span><b>Name</b>: " + contactForm.name + "</span><br>"
+ "<span><b>Email</b>: " + contactForm.email + "</span><br>"
+ "<p>" + contactForm.message + "</p>"
+ "</div>";
email.recipients = admins;
return email;
}
public static composeEmailForDevelop(contactForm: any, admins: any, user: User): Email {
let email: Email = new Email();
email.subject = "OpenAIRE - Develop [" + properties.environment.toUpperCase() + "]";
@ -317,6 +330,26 @@ export class Composer {
return message;
}
public static composeMessageForIrishDashboard(name: string, recipient: string, role: "manager" | "member") {
let email: Email = new Email();
email.subject = 'National Open Access Monitor Ireland | ' + name;
email.recipient = recipient;
email.body = '<p>Dear user,</p>' +
'<p>You have been invited to be a ' + role +' of the for the National Open Access Monitor, Ireland dashboard for the ' + name + '.</p>' +
'<p>Click <a href="((__link__))" target="_blank">this URL</a> and use the verification code below to accept the invitation.</p>' +
'<p>The verification code is <b>((__code__))</b>.</p>' +
'<p>At your first sign in you will be asked to accept and consent to the "OpenAIRE Personal Data Protection Policy and Consent Form" to be able to use the service.</p>' +
(role === "manager"?
'<p>As a manager of the National Open Access Monitor, Ireland, you will have access to the administration part of the dashboard, where you will be able to also invite other users to become managers.</p>':
'<p>As a member of the OpenAIRE Monitor Dashboard, you will have access to the restricted access areas of the profile for the ' + name + '.') +
'<p>Please contact us at <a href="mailto:' + properties.helpdeskEmail+'">' + properties.helpdeskEmail +
'</a> if you have any questions or concerns.</p>' +
'<p>Kind Regards<br>The OpenAIRE Team</p>' +
'<p><a href="' + properties.domain + '" target="_blank">National Open Access Monitor, Ireland</a></p>';
return email;
}
public static composeEmailForCommunityDashboard(name: string, role: "manager" | "member", recipient: string) {
let email: Email = new Email();
email.subject = 'OpenAIRE Connect | ' + name;

View File

@ -1,7 +1,7 @@
import {
Author,
HostedByCollectedFrom,
Journal,
Journal, OARoutes,
Organization,
Project,
RelationResult
@ -96,6 +96,9 @@ export class ResultLandingInfo {
sdg: string[];
eoscSubjects: any[];
oaRoutes: OARoutes;
publiclyFunded: boolean;
// // percentage is for trust
// relatedResearchResults: RelationResult[];
// // percentage is for similarity

View File

@ -1,4 +1,12 @@
import {Author, HostedByCollectedFrom, Journal, Organization, Project, ResultTitle} from "../result-preview/result-preview";
import {
Author,
HostedByCollectedFrom,
Journal,
OARoutes,
Organization,
Project,
ResultTitle
} from "../result-preview/result-preview";
import {Measure, Metric} from "./resultLandingInfo";
export class SearchResult {
@ -61,11 +69,15 @@ export class SearchResult {
compatibilityUNKNOWN: boolean;
countries: string[];
subjects: string[];
originalId: string;
entityType: string;
types: string[];
enermapsId: string;
oaRoutes: OARoutes;
publiclyFunded: boolean;
constructor() {
}

View File

@ -180,7 +180,7 @@ export class EntityActionsComponent implements OnInit {
isRouteAvailable(routeToCheck: string) {
for (let i = 0; i < this.router.config.length; i++) {
let routePath: string = this.router.config[i].path;
if (routePath == routeToCheck) {
if (routePath == routeToCheck || routeToCheck.split('/')[0] == routePath) {
return true;
}
}

View File

@ -47,11 +47,13 @@ export interface StopRule {
@Component({
selector: 'icon',
template: `
<span #svgIcon *ngIf="icon.data" class="uk-icon" [class.uk-preserve]="gradient || icon.preserveColor" [class.uk-flex]="flex" [ngClass]="customClass" [ngStyle]="style" [innerHTML]="icon.data | safeHtml"></span>
<span *ngIf="!icon.data && icon.name" [class.uk-flex]="flex" [ngClass]="customClass" [class.uk-display-inline-block]="!flex">
<span class="material-icons" [ngClass]="type?type:icon.type" [ngStyle]="style">{{icon.name}}</span>
</span>
<span *ngIf="visuallyHidden" class="visually-hidden">{{visuallyHidden}}</span>
<ng-container *ngIf="icon">
<span #svgIcon *ngIf="icon.data" class="uk-icon" [class.uk-preserve]="gradient || icon.preserveColor" [class.uk-flex]="flex" [ngClass]="customClass" [ngStyle]="style" [innerHTML]="icon.data | safeHtml"></span>
<span *ngIf="!icon.data && icon.name" [class.uk-flex]="flex" [ngClass]="customClass" [class.uk-display-inline-block]="!flex">
<span class="material-icons" [ngClass]="type?type:icon.type" [ngStyle]="style">{{icon.name}}</span>
</span>
<span *ngIf="visuallyHidden" class="visually-hidden">{{visuallyHidden}}</span>
</ng-container>
`
})
export class IconsComponent implements AfterViewInit, OnChanges {
@ -149,7 +151,7 @@ export class IconsComponent implements AfterViewInit, OnChanges {
}
initIcon() {
if(this.icon.data && this.svgIcon) {
if(this.icon?.data && this.svgIcon) {
this.cdr.detectChanges();
let svg: Element = this.svgIcon.nativeElement.getElementsByTagName('svg').item(0);
if(!this.defaultSize && svg) {

View File

@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import {LogService} from "./log.service";
@NgModule({
imports: [
CommonModule, FormsModule,
],
declarations: [
],
providers: [ LogService ],
exports: [
]
})
export class LogServiceModule { }

56
utils/log/log.service.ts Normal file
View File

@ -0,0 +1,56 @@
import {HttpClient} from "@angular/common/http";
import {EnvProperties} from '../properties/env-properties';
import {Injectable} from "@angular/core";
import {Observable} from "rxjs";
export abstract class Log{
action: "link" | "orcid-link" | "upload-dois";
message:string;
protected constructor(action, message){
this.action =action;
this.message = message;
}
}
export class LinkLog extends Log{
constructor(sourceTitle,targetTitle) {
super("link","a user linked the \"" +sourceTitle+"\" " + " to the \"" +targetTitle+"\" " );
}
}
export class OrcidLinkLog extends Log{
constructor( action:'added'|'removed', title: string, id: string) {
super("orcid-link","user with ORCID ID " + action + " research product "+ (title?"\"" + title+ "\"":"") + " (" + id + ") " + (action == 'added'?'to':'from')
+ " their ORCID record.");
}
}
export class UploadLog extends Log{
constructor(dois:number) {
super("upload-dois","a user uploaded a list of " + dois +" DOIs to the Irish Monitor to check their presence and retrieve the Open Access types and additional key metadata");
}
}
@Injectable()
export class LogService {
constructor(private http: HttpClient) {
}
logUploadDOIs(properties: EnvProperties, dois:number){
return this.http.post(properties.logServiceUrl+"logAction", new UploadLog(dois) );
}
logLink(properties: EnvProperties, sourceTitle,targetTitle){
return this.http.post(properties.logServiceUrl+"logAction", new LinkLog(sourceTitle, targetTitle) );
}
logOrcidLink(properties: EnvProperties, action:'added'|'removed', title: string, doi: string){
return this.http.post(properties.logServiceUrl+"logAction", new OrcidLinkLog(action, title, doi) );
}
logRemoveOrcidLink(properties: EnvProperties, code: string){
return this.http.post(properties.logServiceUrl+"logAction", new OrcidLinkLog('removed',null, code) );
}
getLogs(properties: EnvProperties):Observable<any>{
return this.http.get(properties.logServiceUrl+"log");
}
}

View File

@ -1,4 +1,4 @@
import {Component, ElementRef, EventEmitter, Input, OnInit, Output} from "@angular/core";
import {ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnInit, Output} from "@angular/core";
import {NavigationStart, Router} from "@angular/router";
@Component({
@ -21,7 +21,7 @@ export class MobileDropdownComponent implements OnInit{
public opened: boolean = false;
private static MOBILE_DROPDOWN_CONTAINER = 'mobile-dropdown-container';
constructor(private element: ElementRef, private router: Router) {
constructor(private element: ElementRef, private router: Router, private cdr: ChangeDetectorRef) {
this.router.events.subscribe(event => {
if(event instanceof NavigationStart) {
this.element.nativeElement.remove();
@ -63,6 +63,7 @@ export class MobileDropdownComponent implements OnInit{
close() {
if(this.opened) {
this.opened = false;
this.cdr.detectChanges();
this.onClose.emit();
document.getElementsByTagName('body')[0].setAttribute('style', '');
}

View File

@ -8,7 +8,9 @@ import {ConfigurationService} from "../configuration/configuration.service";
import {switchMap, take} from "rxjs/operators";
@Injectable()
@Injectable({
providedIn: 'root'
})
export class PiwikService {
constructor(private http: HttpClient, private configurationService: ConfigurationService) {
}
@ -69,6 +71,22 @@ export class PiwikService {
}
public doTrackEvent(properties: EnvProperties, title, siteId, pageURL, eventCategory, eventAction, eventName, eventValue = 0): Observable<any> {
let ua = this.getUserAgent();
let referrer = this.getReferrer();
let piwikId = ((siteId != null) ? siteId : properties.piwikSiteId);
if (typeof location !== 'undefined' && piwikId) {
var url =`${properties.piwikBaseUrl}${piwikId}&rec=1&url=${encodeURIComponent(pageURL)}&action_name=${encodeURIComponent(title)}&e_c=${encodeURIComponent(eventCategory)}&e_a=${encodeURIComponent(eventAction)}&e_n=${encodeURIComponent(eventName)}&e_v=${encodeURIComponent(eventValue)}`
+((ua != null && ua.length > 0) ? ('&ua=' + StringUtils.URIEncode(ua)) : '') +
((referrer != null && referrer.length > 0) ? ('&urlref=' + StringUtils.URIEncode(referrer)) : '');
// console.log(trackingApiUrl)
console.log(url)
// return of(new Object()); // for testing
return this.http.get(url, {responseType: 'blob'});
}
}
private getUserAgent() {
if (typeof navigator !== 'undefined') {
return navigator.userAgent;

View File

@ -1,6 +1,6 @@
export type Environment = "development" | "test" | "beta" | "production";
export type Dashboard = "explore" | "connect" | "monitor" | "aggregator" | "eosc" | "developers" | "faircore4eosc";
export type PortalType = "explore" | "connect" | "community" | "monitor" | "funder" | "ri" | "project" | "organization" | "aggregator" | "eosc" | "faircore4eosc";
export type Dashboard = "explore" | "connect" | "monitor" | "aggregator" | "eosc" | "developers" | "faircore4eosc" | "irish";
export type PortalType = "explore" | "connect" | "community" | "monitor" | "funder" | "ri" | "project" | "organization" | "aggregator" | "eosc" | "faircore4eosc" | "country";
export interface EnvProperties {
environment?: Environment;
@ -57,10 +57,8 @@ export interface EnvProperties {
vocabulariesAPI?: string;
piwikBaseUrl?: string;
piwikSiteId?: string;
loginUrl?: string;
loginServiceURL?: string | string[];
registryUrl?: string;
logoutUrl?: string;
userInfoUrl?: string;
developersApiUrl?: string,
cookieDomain?: string;
feedbackmail?: string;
@ -83,6 +81,7 @@ export interface EnvProperties {
baseLink?: string;
baseOpenaireLink?: string;
afterLoginRedirectLink?: string;
myClaimsLink?: string;
searchLinkToResult?: string;
searchLinkToPublication?: string;
searchLinkToProject?: string;
@ -145,6 +144,14 @@ export interface EnvProperties {
eoscDataTransferLoginUrl?;
eoscDataTransferDestinations?;
hasMachineCache?: boolean;
// irish
logFilesPath?:string;
matomoLogFilesPath?:string;
logServiceUrl?:string;
zenodoDumpUrl?:string;
openOrgsUrl?:string;
orcidDiscoverLinksPage?:string;
}
export function checkPropertyValues(properties:EnvProperties){

View File

@ -33,7 +33,8 @@ export let common: EnvProperties = {
csvLimit: 2000,
pagingLimit: 20,
resultsPerPage: 10,
baseLink: "",
baseLink : "",
myClaimsLink: '/myclaims',
searchLinkToResult: "/search/result?id=",
searchLinkToPublication: "/search/publication?articleId=",
searchLinkToProject: "/search/project?projectId=",
@ -84,6 +85,7 @@ export let common: EnvProperties = {
shareInZenodoPage: '/participate/deposit/zenodo',
afterLoginRedirectLink: '/myCommunities',
searchLinkToCommunities: '/search/find/communities',
openOrgsUrl:"https://beta.orgs.openaire.eu",
}
export let commonDev: EnvProperties = {
@ -101,11 +103,9 @@ export let commonDev: EnvProperties = {
orcidAPIURL: "http://duffy.di.uoa.gr:19480/uoa-orcid-service/",
orcidTokenURL: "https://sandbox.orcid.org/oauth/authorize?",
orcidClientId: "APP-A5M3KTX6NCN67L91",
utilsService: "http://dl170.madgik.di.uoa.gr:8000",
utilsService: "http://mpagasas.di.uoa.gr:8000",
vocabulariesAPI: "https://dev-openaire.d4science.org/provision/mvc/vocabularies/",
loginUrl: "http://mpagasas.di.uoa.gr:19080/login-service/openid_connect_login",
userInfoUrl: "http://mpagasas.di.uoa.gr:19080/login-service/userInfo",
logoutUrl: "http://mpagasas.di.uoa.gr:19080/login-service/openid_logout",
loginServiceURL: "http://mpagasas.di.uoa.gr:19080/login-service/",
cookieDomain: ".di.uoa.gr",
feedbackmail: "kostis30fylloy@gmail.com",
cacheUrl: "http://dl170.madgik.di.uoa.gr:3000/get?url=",
@ -123,7 +123,7 @@ export let commonDev: EnvProperties = {
//connect
communitiesAPI: 'https://dev-openaire.d4science.org/openaire/community/communities',
registryUrl: 'http://mpagasas.di.uoa.gr:8080/dnet-openaire-users-1.0.0-SNAPSHOT/api/registry/',
reCaptchaSiteKey: "6LcVtFIUAAAAAB2ac6xYivHxYXKoUvYRPi-6_rLu",
//admin
miningBackendURL: 'https://beta.services.openaire.eu/interactive-mining',
feedbackmailForMissingEntities: 'feedback@openaire.eu',
@ -142,9 +142,7 @@ export let commonTest: EnvProperties = {
csvAPIURL: "https://services.openaire.eu/search/v2/api/reports",
utilsService: "https://explore.openaire.eu/utils-service",
vocabulariesAPI: "https://services.openaire.eu/provision/mvc/vocabularies/",
loginUrl: " https://services.openaire.eu/login-service/openid_connect_login",
userInfoUrl: " https://services.openaire.eu/login-service/userInfo",
logoutUrl: "https://services.openaire.eu/login-service/openid_logout",
loginServiceURL: " https://services.openaire.eu/login-service/",
cacheUrl: "https://explore.openaire.eu/cache/get?url=",
datasourcesAPI: "https://services.openaire.eu/openaire/ds/api/",
monitorServiceAPIURL: "https://services.openaire.eu/uoa-monitor-service",
@ -173,9 +171,7 @@ export let commonBeta: EnvProperties = {
csvAPIURL: "https://beta.services.openaire.eu/search/v2/api/reports",
utilsService: "https://demo.openaire.eu/utils-service",
vocabulariesAPI: "https://beta.services.openaire.eu/provision/mvc/vocabularies/",
loginUrl: "https://beta.services.openaire.eu/login-service/openid_connect_login",
userInfoUrl: "https://beta.services.openaire.eu/login-service/userInfo",
logoutUrl: "https://beta.services.openaire.eu/login-service/openid_logout",
loginServiceURL: "https://beta.services.openaire.eu/login-service/",
cacheUrl: "https://demo.openaire.eu/cache/get?url=",
datasourcesAPI: "https://beta.services.openaire.eu/openaire/ds/api/",
monitorServiceAPIURL: "https://beta.services.openaire.eu/uoa-monitor-service",
@ -214,9 +210,7 @@ export let commonProd: EnvProperties = {
csvAPIURL: "https://services.openaire.eu/search/v2/api/reports",
utilsService: "https://explore.openaire.eu/utils-service",
vocabulariesAPI: "https://services.openaire.eu/provision/mvc/vocabularies/",
loginUrl: " https://services.openaire.eu/login-service/openid_connect_login",
userInfoUrl: " https://services.openaire.eu/login-service/userInfo",
logoutUrl: "https://services.openaire.eu/login-service/openid_logout",
loginServiceURL: "https://services.openaire.eu/login-service/",
cacheUrl: "https://explore.openaire.eu/cache/get?url=",
datasourcesAPI: "https://services.openaire.eu/openaire/ds/api/",
monitorServiceAPIURL: "https://services.openaire.eu/uoa-monitor-service",
@ -240,6 +234,9 @@ export let commonProd: EnvProperties = {
deleteCacheUrl: 'https://explore.openaire.eu/cache/clear',
deleteBrowserCacheUrl: 'https://services.openaire.eu/uoa-admin-tools/cache',
connectPortalUrl: 'https://connect.openaire.eu',
//irish
openOrgsUrl:"https://orgs.openaire.eu"
}

Some files were not shown because too many files have changed in this diff Show More