Compare commits

...

28 Commits

Author SHA1 Message Date
argirok 1882ea10fa updates after merge 2024-04-10 13:20:55 +03:00
argirok f5414efaac Merge remote-tracking branch 'origin/develop' into plugins-functionality
# Conflicts:
#	role-verification/role-verification.component.ts
2024-04-10 13:11:37 +03:00
argirok 609f09cc9f [develop | DONE | CHANGED] Claims pass idSuffix for generating openAIRE id 2024-04-10 13:07:35 +03:00
argirok 275b94dec3 Merge branch 'develop' of code-repo.d4science.org:MaDgIK/openaire-library into develop 2024-04-10 12:27:12 +03:00
argirok f5850aa781 [pluginsFunctionality | DONE | FIXED] Plugin Openaire services: fix no services appear 2024-04-09 10:48:23 +03:00
Konstantinos Triantafyllou 4f9f2f2505 [develop]: Add stats profile in form of an indicator even if it is null 2024-04-08 18:57:49 +03:00
Konstantinos Triantafyllou 0111ea6681 [develop]: Role verification reload page instead of removing params. 2024-04-08 17:14:00 +03:00
Konstantinos Triantafyllou c69d1cd94d [develop]: Role verification improve redirect. 2024-04-08 16:44:47 +03:00
Konstantinos Triantafyllou 901f9f769d [develop]: Fix an error while inviting a person that it is already a member or manager. Remove message while accepting the member role. 2024-04-08 15:17:53 +03:00
Konstantinos Triantafyllou e6d03d324f Merge branch 'develop' of code-repo.d4science.org:MaDgIK/openaire-library into develop 2024-04-08 14:14:18 +03:00
Konstantinos Triantafyllou 69cdb2cec2 [develop]: Add roles in stakeholder configuration to handle naming of roles in different services. 2024-04-08 14:14:09 +03:00
Konstantinos Triantafyllou 4f10c5c5f4 [develop]: Align import/export indicators with multi indicator paths. 2024-04-06 03:10:51 +03:00
Konstantinos Triantafyllou e91830f2f8 [develop]: Monitor base component delete chartsActiveType 2024-04-05 20:31:29 +03:00
Konstantinos Triantafyllou 0fb20de588 Merge remote-tracking branch 'origin/new-reorder' into develop 2024-04-05 20:13:24 +03:00
Konstantinos Triantafyllou b71bdfa559 [new-reorder]: Add multi charts in admin indicators (works now only for charts in UI). Deprecated transition-group disable/enable method. 2024-04-05 20:13:05 +03:00
Konstantina Galouni dc1679c565 [develop | DONE | CHANGED]: newSearchPage.component.html: Restored number of results and keyword display & added uk-text-capitalize on selected filter labels. 2024-04-05 11:09:16 +03:00
Konstantinos Triantafyllou d63092cd9e [new-order | DONE]: Add new move indicator method when an indicator moved to another section 2024-04-05 10:57:39 +03:00
Konstantina Galouni c351349f8e [develop | DONE | FIXED] newSearchPage.component.html: Added checks, not to show "Filters" column in the search pages, when there are no filters. 2024-04-04 17:13:56 +03:00
Konstantina Galouni dcfc6b0b6a [develop | DONE | CHANGED]: searchFields.base.ts: Replace in ORGANIZATION_REFINE_FIELDS and ORGANIZATION_ADVANCED_FIELDS field "country" with "countrynojurisdiction". 2024-04-04 16:10:08 +03:00
Konstantina Galouni 07569f24e1 [develop | DONE | FIXED]: resultLanding.component.html & searchResult.component.ts: Added checks for ORCID also for properties.dashboard == "irish". 2024-04-03 16:16:09 +03:00
Konstantina Galouni 18791ec9d2 [develop | DONE | ADDED]: searchFields.base.ts: Added "pid" field in ORGANIZATION_ADVANCED_FIELDS. 2024-04-02 13:33:23 +03:00
Konstantina Galouni 78d262dd3b [develop | DONE | ADDED]: Added search organizations by PID (ror, isni, wikidata, fundRef).
1. string-utils.class.ts: Added methods "isValidRor()", "isValidIsni()", "isValidWikidata()", "isValidFundRef()" and cases for these new identifiers.
2. newSearchPage.component.ts: In method "createKeywordQuery()", check for PIDs also for organizations entity.
3. environment.ts: Updated property "fundRefURL" to "https://data.crossref.org/fundingdata/funder/" (old: https://api.crossref.org/funders/).
2024-04-02 10:59:33 +03:00
Konstantinos Triantafyllou 828dfc0671 [develop]: Remove topics from exported file in export indicators. 2024-04-01 18:56:26 +03:00
Konstantinos Triantafyllou b34f42326c [develop | ADDED]: Add loading in import indicators in order to avoid any actions during this process 2024-04-01 18:16:21 +03:00
Konstantinos Triantafyllou e8ec49a69b Merge branch 'develop' of code-repo.d4science.org:MaDgIK/openaire-library into develop 2024-04-01 01:16:39 +03:00
Konstantinos Triantafyllou 0c75394621 [develop | FIXED | CHANGED]: Add stakeholder in export indicators file and import base on this stakeholder. 2024-04-01 01:16:31 +03:00
argirok d6b924bcaa [develop | DONE | FIXED] linking update check to decide if claim is pending and show the link to landing 2024-03-21 09:47:02 +02:00
argirok 95097beaae [develop | DONE | FIXED] linking update parsing of the datacite API 2024-03-21 09:46:11 +02:00
27 changed files with 1249 additions and 1005 deletions

View File

@ -92,6 +92,7 @@ export class ClaimRecord2Insert {
targetAccessRights: string;
targetEmbargoEndDate: string;
claimedInDashboard: string;
idSuffix:string;
constructor() {

View File

@ -364,7 +364,7 @@ export class DisplayClaimsComponent implements OnInit, OnDestroy {
if (claimDateStr < lastUpdateDateStr) {
return true;
} else {
return claim.target.collectedFrom != "infrastruct_::openaire" && claim.indexed;
return claim.target.collectedFrom != "infrastruct_::openaire";
}
}

View File

@ -58,22 +58,20 @@ export class SearchDataciteService {
entity.result.journal = null;
entity.result.DOI = item.attributes.doi;
entity.id = item.attributes.doi;
entity.title = item.attributes.title;
entity.title = Array.isArray(item.attributes.titles) && item.attributes.titles[0].title?item.attributes.titles[0].title:null;
entity.result.url = properties.doiURL + item.attributes.doi;
entity.result.source = 'datacite';
entity.type = 'dataset';
entity.result.date = item.attributes.published;
entity.result.date = item.attributes.publicationYear;
entity.result.accessRights = "OPEN";
entity.result.publisher = item.attributes['container-title'];
entity.result.publisher = item.attributes['publisher'];
entity.result.journal = null;
entity.result.record = item;
if (item.attributes.author) {
if (item.attributes.creators) {
entity.result.authors = [];
for (let j = 0; j < item.attributes.author.length; j++) {
const author = item.attributes.author[j];
if(author.family || author.literal) {
entity.result.authors.push((author.family) ? author.family + (author.given ? ', ' + author.given : '') : author.literal);
}
for (let j = 0; j < item.attributes.creators.length; j++) {
const author = item.attributes.creators[j].name;
entity.result.authors.push(author);
}
}
results.push(entity);

View File

@ -156,13 +156,14 @@ export class ClaimInsertComponent {
this.loading.open();
let claims: ClaimRecord2Insert[] = [];
let directclaims: DirectIndexRecord[] = [];
let idSuffix = (new Date()).getTime() + "";
let dashboard = this.properties.environment+"_"+this.properties.dashboard + (this.communityId?("_"+this.communityId):'');
for (let j = 0; j < this.sources.length; j++) { // if an external result -> direct insert in the index
const result: ClaimEntity = this.sources[j];
if (result.result && ["crossref", "datacite", "orcid"].indexOf(result.result.source) != -1) {
directclaims.push({
"id": result.id,
"record": ClaimInsertComponent.createDirectClaim(result, this.results)
"record": ClaimInsertComponent.createDirectClaim(result, this.results, idSuffix)
});
}
}
@ -173,12 +174,12 @@ export class ClaimInsertComponent {
if (this.sources.length > 0) {
directclaims.push({
"id": entity.id,
"record": ClaimInsertComponent.createDirectClaim(entity, this.sources)
"record": ClaimInsertComponent.createDirectClaim(entity, this.sources, idSuffix)
});
} else if (this.inlineEntity) {
directclaims.push({
"id": entity.id,
"record": ClaimInsertComponent.createDirectClaim(entity, [this.inlineEntity])
"record": ClaimInsertComponent.createDirectClaim(entity, [this.inlineEntity], idSuffix)
});
}
@ -187,11 +188,11 @@ export class ClaimInsertComponent {
for (let j = 0; j < this.sources.length; j++) {
const result: ClaimEntity = this.sources[j]; // this is a research result
if (entity.result) {
claims.push(ClaimInsertComponent.createResultClaim(result, entity, user.email, dashboard));
claims.push(ClaimInsertComponent.createResultClaim(result, entity, user.email, dashboard, idSuffix));
} else if (entity.context) {
claims.push(ClaimInsertComponent.createContextClaim(result, entity, user.email, dashboard));
claims.push(ClaimInsertComponent.createContextClaim(result, entity, user.email, dashboard, idSuffix));
} else if (entity.project) {
claims.push(ClaimInsertComponent.createProjectClaim(result, entity, user.email, dashboard));
claims.push(ClaimInsertComponent.createProjectClaim(result, entity, user.email, dashboard, idSuffix));
}
this.infoToLog.push([ result.title?result.title: result.id, entity.title?entity.title:entity.id]);
@ -201,15 +202,15 @@ export class ClaimInsertComponent {
if (this.inlineEntity.result) {
if (entity.result) {
claims.push(ClaimInsertComponent.createResultClaim(this.inlineEntity, entity, user.email, dashboard));
claims.push(ClaimInsertComponent.createResultClaim(this.inlineEntity, entity, user.email, dashboard, idSuffix));
} else if (entity.context) {
claims.push(ClaimInsertComponent.createContextClaim(this.inlineEntity, entity, user.email, dashboard));
claims.push(ClaimInsertComponent.createContextClaim(this.inlineEntity, entity, user.email, dashboard, idSuffix));
} else if (entity.project) {
claims.push(ClaimInsertComponent.createProjectClaim(this.inlineEntity, entity, user.email, dashboard));
claims.push(ClaimInsertComponent.createProjectClaim(this.inlineEntity, entity, user.email, dashboard, idSuffix));
}
} else if (this.inlineEntity.project) {
if (entity.result) {
claims.push(ClaimInsertComponent.createProjectClaim(entity, this.inlineEntity, user.email, dashboard));
claims.push(ClaimInsertComponent.createProjectClaim(entity, this.inlineEntity, user.email, dashboard, idSuffix));
}
}
}
@ -390,7 +391,7 @@ export class ClaimInsertComponent {
}
private static createContextClaim(resultEntity: ClaimEntity, contextEntity: ClaimEntity, user: any, dashboard:string): ClaimRecord2Insert {
private static createContextClaim(resultEntity: ClaimEntity, contextEntity: ClaimEntity, user: any, dashboard:string, idSuffix:string): ClaimRecord2Insert {
return {
claimedBy: user,
sourceId: contextEntity.context.concept.id,
@ -403,11 +404,12 @@ export class ClaimInsertComponent {
targetCollectedFrom: resultEntity.result.source,
targetAccessRights: resultEntity.result.accessRights,
targetEmbargoEndDate: ClaimInsertComponent.getEmbargoEndDate(resultEntity),
claimedInDashboard : dashboard
claimedInDashboard : dashboard,
idSuffix : idSuffix
};
}
private static createProjectClaim(resultEntity: ClaimEntity, projectEntity: ClaimEntity, user: any, dashboard:string): ClaimRecord2Insert {
private static createProjectClaim(resultEntity: ClaimEntity, projectEntity: ClaimEntity, user: any, dashboard:string, idSuffix:string): ClaimRecord2Insert {
return {
claimedBy: user,
sourceId: projectEntity.id,
@ -420,11 +422,12 @@ export class ClaimInsertComponent {
targetCollectedFrom: resultEntity.result.source,
targetAccessRights: resultEntity.result.accessRights,
targetEmbargoEndDate: ClaimInsertComponent.getEmbargoEndDate(resultEntity),
claimedInDashboard : dashboard
claimedInDashboard : dashboard,
idSuffix : idSuffix
};
}
private static createResultClaim(inlineResult: ClaimEntity, resultEntity: ClaimEntity, user: string, dashboard:string): ClaimRecord2Insert {
private static createResultClaim(inlineResult: ClaimEntity, resultEntity: ClaimEntity, user: string, dashboard:string, idSuffix:string): ClaimRecord2Insert {
return {
claimedBy: user,
@ -438,7 +441,9 @@ export class ClaimInsertComponent {
targetCollectedFrom: inlineResult.result.source,
targetAccessRights: inlineResult.result.accessRights,
targetEmbargoEndDate: ClaimInsertComponent.getEmbargoEndDate(inlineResult),
claimedInDashboard : dashboard
claimedInDashboard : dashboard,
idSuffix : idSuffix
};
}
@ -448,12 +453,13 @@ export class ClaimInsertComponent {
}
return ""
}
static createDirectClaim(resultEntity: ClaimEntity, results: ClaimEntity[]) {
static createOpenAIREId(id, idSuffix:string):string {
return id.indexOf( "::" ) == -1 ? ("userclaim___::" + Md5.hashStr(id + idSuffix)):id;
}
static createDirectClaim(resultEntity: ClaimEntity, results: ClaimEntity[], idSuffix:string) {
let entity = {};
const md5_id = Md5.hashStr(resultEntity.id);
entity["originalId"] = "userclaim___::" + md5_id;
entity["openaireId"] = "userclaim___::" + md5_id;
entity["originalId"] = this.createOpenAIREId(resultEntity.id, idSuffix);
entity["openaireId"] = this.createOpenAIREId(resultEntity.id, idSuffix);
entity["title"] = resultEntity.title;
entity["title"] = (Array.isArray(resultEntity.title) && resultEntity.title.length > 0) ? resultEntity.title[0] : resultEntity.title;

View File

@ -370,10 +370,8 @@ export class MenuComponent implements OnInit {
}
public valueChange() {
this.elements.disable();
this.cdr.detectChanges();
this.elements.init();
this.elements.enable();
}
public get displayMenuItems() {

View File

@ -12,10 +12,10 @@ export class PluginOpenAIREProducts extends PluginBaseInfo{
@Component({
selector: 'plugin-openaire-products',
template: `
<div *ngIf="pluginObject" class="uk-container uk-section">
<div *ngIf="pluginObject " class="uk-container uk-section">
<h3>{{pluginObject.title}} </h3>
<div *ngIf="services" uk-slider class="uk-slider">
<div *ngIf="services && servicesToShow" uk-slider class="uk-slider">
<ul *ngIf="slides" class="uk-slider-items" uk-height-match="target: .uk-card; row: false">
<li *ngFor="let slide of [].constructor(slides); let i=index" class="uk-width-1-1 uk-padding">
<div class="uk-grid uk-child-width-1-3@m uk-child-width-1-1" uk-grid uk-scrollspy="target: [uk-scrollspy-class]; cls: uk-animation-fade; repeat: true">
@ -51,7 +51,7 @@ export class PluginOpenAIREProducts extends PluginBaseInfo{
export class PluginOpenaireProductsComponent extends PluginBaseComponent<PluginOpenAIREProducts>{
services = null;
excludedServiceIds = ["openaire_login","research_community_dashboard"]
servicesToShow = [];
servicesToShow = null;
slides = 1;
slideItems = 3;
api= "https://catalogue.openaire.eu/api/catalogue-resources?from=0&quantity=100&order=asc&orderField=name";
@ -64,7 +64,9 @@ export class PluginOpenaireProductsComponent extends PluginBaseComponent<PluginO
if(!this.services) {
this.subscriptions.push(this.http.get( this.properties.cacheUrl + encodeURIComponent(this.api)).subscribe(res =>{
this.services = res["results"].map( x=> {
x.id = x.id.split("openaire.")[1]
if(x.id.indexOf("openaire.")!=-1){
x.id = x.id.split("openaire.")[1]
}
return x;
});
this.services = this.services.filter(x=> this.excludedServiceIds.indexOf(x.id) ==-1);

View File

@ -22,6 +22,7 @@ export class MonitorComponent extends MonitorIndicatorStakeholderBaseComponent {
stakeholder = null;
@Input() pluginObject: PluginStats;
profiles;
public chartsActiveType: Map<string, IndicatorPath> = new Map<string, IndicatorPath>();
constructor(protected _route: ActivatedRoute,
protected _router: Router,
protected _meta: Meta,

View File

@ -8,26 +8,26 @@
<div class="uk-flex uk-flex-left@m uk-flex-center uk-width-expand">
<ul class="uk-subnav uk-subnav-pill">
<li [class.uk-active]="showCurrent" (click)="showCurrent = true">
<a class="uk-text-capitalize">{{role}}s</a>
<a class="uk-text-capitalize">{{stakeholderUtils.roles[role]}}s</a>
</li>
<li [class.uk-active]="!showCurrent" (click)="showCurrent = false">
<a>Pending {{role}}s</a>
<a>Pending {{stakeholderUtils.roles[role]}}s</a>
</li>
</ul>
</div>
<div class="uk-width-expand@m uk-width-1-1 uk-grid uk-flex-right@m uk-flex-center uk-flex-middle" uk-grid>
<div *ngIf="showCurrent" [disabled]="loadActive" search-input class="uk-width-expand@l uk-width-1-1"
[searchControl]="filterForm.get('active')" [expandable]="true" [placeholder]="'Search ' + role + 's'" searchInputClass="outer">
[searchControl]="filterForm.get('active')" [expandable]="true" [placeholder]="'Search ' + stakeholderUtils.roles[role] + 's'" searchInputClass="outer">
</div>
<div *ngIf="!showCurrent" [disabled]="loadPending" search-input class="uk-width-expand@l uk-width-1-1"
[searchControl]="filterForm.get('pending')" [expandable]="true" [placeholder]="'Search invitations'" searchInputClass="outer">
</div>
<div>
<button *ngIf="exists" class="uk-button uk-button-default uk-flex uk-flex-middle"
<button *ngIf="canInvite" class="uk-button uk-button-default uk-flex uk-flex-middle"
[attr.uk-tooltip]="inviteDisableMessage" [class.uk-disabled]="loadPending || loadPending"
[disabled]="loadActive || loadPending || !!inviteDisableMessage" (click)="openInviteModal()">
<icon name="person_add" [flex]="true" type="filled"></icon>
<span class="uk-margin-small-left uk-text-bold uk-text-uppercase">Invite {{role}}</span>
<span class="uk-margin-small-left uk-text-bold uk-text-uppercase">Invite {{stakeholderUtils.roles[role]}}</span>
</button>
<button *ngIf="!exists && isCurator" class="uk-button uk-button-default uk-flex uk-flex-middle"
(click)="openCreateRoleModal()">
@ -47,16 +47,16 @@
<div *ngIf="!loadActive && !loadPending">
<div *ngIf="(showCurrent && showActive.length == 0) || (!showCurrent && showPending.length == 0)"
class="uk-card uk-card-default uk-padding-large uk-text-center uk-margin-bottom uk-text-bold">
<div *ngIf="showCurrent">No {{role}}s found</div>
<div *ngIf="!showCurrent">No pending {{role}} invitations found</div>
<div *ngIf="showCurrent">No {{stakeholderUtils.roles[role]}}s found</div>
<div *ngIf="!showCurrent">No pending {{stakeholderUtils.roles[role]}} invitations found</div>
</div>
<div *ngIf="(showCurrent && showActive.length > 0) || (!showCurrent && showPending.length > 0)">
<no-load-paging *ngIf="showCurrent" [type]="(showActive.length > 1)?(role + 's'):role"
<no-load-paging *ngIf="showCurrent" [type]="(showActive.length > 1)?(stakeholderUtils.roles[role] + 's'):role"
(pageChange)="updateActivePage($event)"
[page]="activePage" [pageSize]="pageSize"
[totalResults]="showActive.length">
</no-load-paging>
<no-load-paging *ngIf="!showCurrent" [type]="role + ' ' + (showPending.length > 1?'invitations':'invitation')"
<no-load-paging *ngIf="!showCurrent" [type]="stakeholderUtils.roles[role] + ' ' + (showPending.length > 1?'invitations':'invitation')"
(pageChange)="updatePendingPage($event)"
[page]="pendingPage" [pageSize]="pageSize"
[totalResults]="showPending.length">
@ -109,12 +109,12 @@
</modal-alert>
<modal-alert #deleteModal [overflowBody]="false" (alertOutput)="deleteActive()" classTitle="uk-background-primary uk-light">
<div *ngIf="selectedUser">
Are you sure you want to remove <span class="uk-text-bold">{{selectedUser}}</span> from {{role}}s?
Are you sure you want to remove <span class="uk-text-bold">{{selectedUser}}</span> from {{stakeholderUtils.roles[role]}}s?
</div>
</modal-alert>
<modal-alert #deletePendingModal [overflowBody]="false" (alertOutput)="deletePending()" classTitle="uk-background-primary uk-light">
<div *ngIf="selectedUser">
Are you sure you want to cancel {{role}} invitation of <span class="uk-text-bold">{{selectedUser}}</span>?
Are you sure you want to cancel {{stakeholderUtils.roles[role]}} invitation of <span class="uk-text-bold">{{selectedUser}}</span>?
</div>
</modal-alert>
<modal-alert #createRoleModal [overflowBody]="false" (alertOutput)="createGroup()" classTitle="uk-background-primary uk-light"

View File

@ -23,6 +23,7 @@ import {NotificationHandler} from "../../../utils/notification-handler";
import {ClearCacheService} from "../../../services/clear-cache.service";
import {catchError, map, tap} from "rxjs/operators";
import {InputComponent} from "../../../sharedComponents/input/input.component";
import {StakeholderUtils} from "../../../monitor-admin/utils/indicator-utils";
class InvitationResponse {
email: string;
@ -53,6 +54,8 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
@Input()
public deleteAuthorizationLevel: 'curator' | 'manager' = 'curator';
@Input()
public inviteAuthorizationLevel: 'curator' | 'manager' = 'manager';
@Input()
public message: string = null;
@Input()
public emailComposer: Function;
@ -86,12 +89,12 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
@ViewChild('deleteModal') deleteModal: AlertModal;
@ViewChild('deletePendingModal') deletePendingModal: AlertModal;
@ViewChild('createRoleModal') createRoleModal: AlertModal;
public stakeholderUtils: StakeholderUtils = new StakeholderUtils();
constructor(private userRegistryService: UserRegistryService,
private userManagementService: UserManagementService,
private clearCacheService: ClearCacheService,
private notificationService: NotificationService,
private router: Router,
private cdr: ChangeDetectorRef,
private fb: UntypedFormBuilder) {
}
@ -182,7 +185,7 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
openDeleteModal(item: any) {
if (this.showCurrent) {
this.selectedUser = item.email;
this.deleteModal.alertTitle = 'Delete ' + this.role;
this.deleteModal.alertTitle = 'Delete ' + this.stakeholderUtils.roles[this.role];
this.deleteModal.open();
} else {
this.selectedUser = item;
@ -192,7 +195,7 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
}
openInviteModal() {
this.inviteModal.alertTitle = 'Invite ' + this.role;
this.inviteModal.alertTitle = 'Invite ' + this.stakeholderUtils.roles[this.role];
this.inviteModal.okButtonLeft = false;
this.inviteModal.okButtonText = 'Send';
this.emailsForm = this.fb.array([], Validators.required);
@ -242,7 +245,7 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
if (this.currentPendingPage.length === 0) {
this.pendingPage = 1;
}
NotificationHandler.rise(StringUtils.capitalize(this.role) + ' invitation to ' + this.selectedUser + ' has been <b>canceled</b>');
NotificationHandler.rise(StringUtils.capitalize(this.stakeholderUtils.roles[this.role]) + ' invitation to ' + this.selectedUser + ' has been <b>canceled</b>');
this.loadPending = false;
}, error => {
NotificationHandler.rise('An error has occurred. Please try again later', 'danger');
@ -266,9 +269,9 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
return this.userRegistryService.invite(this.type, this.id, {
link: this.link,
email: this.emailComposer(this.name, email, this.role)
}, this.role).pipe(map(invitation => new InvitationResponse(email, invitation), catchError(error => {
return of(new InvitationResponse(current, null));
})));
}, this.role).pipe(catchError(error => {
return of(null);
}), map(invitation => new InvitationResponse(email, invitation)));
});
this.subs.push(forkJoin(invitations).subscribe(responses => {
let notifications = responses.map(response => {
@ -290,7 +293,8 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
return of(null);
}
} else {
NotificationHandler.rise('An error has occurred while sending the invitation mail to ' + response.email + '.Please try again later', 'danger');
NotificationHandler.rise('An error has occurred while sending the invitation mail to ' +
response.email + '.Check if the user is already a ' + this.stakeholderUtils.roles[this.role] + ' or try again later', 'danger');
return of(null);
}
});
@ -310,7 +314,7 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
this.updateLists();
}, error => {
if(error.status === 409) {
NotificationHandler.rise('Group already exists. You can try to invite a ' + this.role + ' instead.', 'warning');
NotificationHandler.rise('Group already exists. You can try to invite a ' + this.stakeholderUtils.roles[this.role] + ' instead.', 'warning');
this.updateLists();
} else {
NotificationHandler.rise('An error has occurred. Please try again later', 'danger');
@ -323,6 +327,10 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
public get canDelete() {
return (this.deleteAuthorizationLevel === 'curator'?this.isCurator:this.isManager);
}
public get canInvite() {
return this.exists && (this.inviteAuthorizationLevel === 'curator'?this.isCurator:this.isManager);
}
public isMe(userId: string) {
return userId && userId.includes(this.user.id) && !this.isCurator;

View File

@ -79,7 +79,8 @@
[type]="resultLandingInfo.resultType"
[result]="resultLandingInfo" [id]="resultLandingInfo.objIdentifier">
<!-- ORCID -->
<div *ngIf="properties.adminToolsPortalType == 'explore' || properties.adminToolsPortalType == 'community' || properties.adminToolsPortalType == 'aggregator'"
<div *ngIf="properties.adminToolsPortalType == 'explore' || properties.adminToolsPortalType == 'community'
|| properties.adminToolsPortalType == 'aggregator' || properties.dashboard == 'irish'"
class="uk-margin-small-right">
<orcid-work [resultId]="id" [resultTitle]="resultLandingInfo.title" [resultLandingInfo]="resultLandingInfo"
[pids]="pidsArrayString" [pageType]="'landing'">
@ -544,7 +545,8 @@
</div>
<hr class="uk-margin-remove">
</ng-container>
<ng-container *ngIf="properties.adminToolsPortalType == 'explore' || properties.adminToolsPortalType == 'community' || properties.adminToolsPortalType == 'aggregator'" >
<ng-container *ngIf="properties.adminToolsPortalType == 'explore' || properties.adminToolsPortalType == 'community'
|| properties.adminToolsPortalType == 'aggregator' || properties.dashboard == 'irish'" >
<div class="uk-padding-small uk-padding-remove-horizontal ">
<orcid-work [resultId]="id" [resultTitle]="resultLandingInfo?.title" [resultLandingInfo]="resultLandingInfo"
[pids]="pidsArrayString" [pageType]="'landing'" [isMobile]="true"

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@ import {
Visibility
} from "../../monitor/entities/stakeholder";
import {
AbstractControl,
AbstractControl, FormArray, FormGroup,
UntypedFormArray,
UntypedFormBuilder,
UntypedFormControl,
@ -31,8 +31,8 @@ import {AlertModal} from "../../utils/modal/alert";
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 {Observable, Subscriber} from "rxjs";
import {MoveIndicator, SectionInfo, StakeholderService} from "../../monitor/services/stakeholder.service";
import {BehaviorSubject, Observable, Subscriber} from "rxjs";
import {LayoutService} from "../../dashboard/sharedComponents/sidebar/layout.service";
import {Router} from "@angular/router";
import {Role, Session, User} from "../../login/utils/helper.class";
@ -45,6 +45,7 @@ import {NotificationHandler} from "../../utils/notification-handler";
import {IndicatorStakeholderBaseComponent} from "../utils/stakeholder-base.component";
import {properties} from "../../../../environments/environment";
import {StatsProfilesService} from "../utils/services/stats-profiles.service";
import {TransitionGroupComponent} from "../../utils/transition-group/transition-group.component";
declare var UIkit;
declare var copy;
@ -82,12 +83,13 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
public index: number = -1;
public editing: boolean = false;
public dragging: boolean = false;
/* Reorder indicators */
public to: BehaviorSubject<SectionInfo> = new BehaviorSubject<SectionInfo>(null);
/** Caches */
public safeUrls: Map<string, SafeResourceUrl> = new Map<string, SafeResourceUrl>([]);
public numberResponses: Map<string, any> = new Map<string, any>();
public numberResults: Map<string, number> = new Map<string, number>();
/** Import / Export Indicators */
importLoading: boolean = false;
public loading: boolean = false;
@ViewChild('editChartModal', {static: true}) editChartModal: AlertModal;
@ViewChild('editNumberModal', {static: true}) editNumberModal: AlertModal;
@ViewChild('deleteModal', {static: true}) deleteModal: AlertModal;
@ -95,12 +97,12 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
public sectionTypeToDelete: string;
public sectionChildrenActionOnDelete: string;
public indicatorChildrenActionOnDelete: string;
urlParameterizedMessage = null;
showCheckForSchemaEnhancements: boolean = false;
private notification: Notification;
@ViewChild('editNumberNotify', {static: true}) editNumberNotify: NotifyFormComponent;
@ViewChild('editChartNotify', {static: true}) editChartNotify: NotifyFormComponent;
@ViewChild('deleteNotify', {static: true}) deleteNotify: NotifyFormComponent;
/* Transition Groups */
@ViewChild('transitionGroup') transitionGroup: TransitionGroupComponent;
public isFullscreen: boolean = false;
@ -125,7 +127,6 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
private notificationService: NotificationService,
private fb: UntypedFormBuilder,
protected _router: Router,
private cdr: ChangeDetectorRef,
private sanitizer: DomSanitizer) {
super()
this.filesToUpload = [];
@ -187,7 +188,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
}
});
if (document !== undefined) {
let callback = (list, type: IndicatorType, action: 'moved' | 'added' | 'removed'): void => {
let callback = (list): string[] => {
let items: HTMLCollection = list.current.children;
let reordered = [];
for (let i = 0; i < items.length; i++) {
@ -195,12 +196,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
reordered.push(items.item(i).id);
}
}
let reorder: Reorder = {
action: action,
target: list.detail[1].id,
ids: reordered
}
this.reorderIndicators(list.current.id.toString().split('-')[1], type, reorder);
return reordered;
};
this.numbers.forEach((section) => {
this.subscriptions.push(UIkit.util.on(document, 'start', '#number-' + section._id, (): void => {
@ -210,24 +206,32 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
this.dragging = false;
}));
this.subscriptions.push(UIkit.util.on(document, 'moved', '#number-' + section._id, (list): void => {
callback(list, "number", 'moved');
this.reorderIndicators(section._id, 'number', callback(list));
}));
this.subscriptions.push(UIkit.util.on(document, 'added', '#number-' + section._id, (list): void => {
callback(list, "number", 'added');
this.to.next({id: section._id, indicators: callback(list)});
}));
this.subscriptions.push(UIkit.util.on(document, 'removed', '#number-' + section._id, (list): void => {
callback(list, "number", 'removed');
let sub = this.to.asObservable().subscribe(to => {
if (to) {
let from: SectionInfo = {id: section._id, indicators: callback(list)};
this.moveIndicator({target: list.detail[1].id, from: from, to: to});
setTimeout(() => {
sub.unsubscribe();
})
}
})
}));
});
this.charts.forEach((section) => {
this.subscriptions.push(UIkit.util.on(document, 'moved', '#chart-' + section._id, (list): void => {
callback(list, "chart", 'moved');
this.reorderIndicators(section._id, 'chart', callback(list));
}));
this.subscriptions.push(UIkit.util.on(document, 'added', '#chart-' + section._id, (list): void => {
callback(list, "chart", 'added');
//callback(list, "chart", 'added');
}));
this.subscriptions.push(UIkit.util.on(document, 'removed', '#chart-' + section._id, (list): void => {
callback(list, "chart", 'removed');
// callback(list, "chart", 'removed');
}));
});
}
@ -264,7 +268,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
setNumbers() {
this.numberSections = this.fb.array([]);
this.numberResults.clear();
let urls: Map<string, [number, number][]> = new Map<string, [number, number][]>();
let urls: Map<string, [number, number, number][]> = new Map<string, [number, number, number][]>();
this.numbers.forEach((section, i) => {
this.numberSections.push(this.fb.group({
_id: this.fb.control(section._id),
@ -276,11 +280,13 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
indicators: this.fb.control(section.indicators)
}));
section.indicators.forEach((number, j) => {
let url = this.indicatorUtils.getFullUrl(this.stakeholder, 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);
number.indicatorPaths.forEach((indicatorPath, k) => {
let url = this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath);
const pair = JSON.stringify([indicatorPath.source, url]);
const indexes = urls.get(pair) ? urls.get(pair) : [];
indexes.push([i, j, k]);
urls.set(pair, indexes);
});
});
});
this.numberSubscription.forEach(value => {
@ -302,10 +308,10 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
});
}
private calculateResults(response: any, indexes: [number, number][]) {
indexes.forEach(([i, j]) => {
private calculateResults(response: any, indexes: [number, number, number][]) {
indexes.forEach(([i, j, k]) => {
let result = JSON.parse(JSON.stringify(response));
this.numbers[i].indicators[j].indicatorPaths[0].jsonPath.forEach(jsonPath => {
this.numbers[i].indicators[j].indicatorPaths[k].jsonPath.forEach(jsonPath => {
if (result) {
result = result[jsonPath];
}
@ -318,14 +324,14 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
} else {
result = 0;
}
this.numberResults.set(i + '-' + j, result);
this.numberResults.set(i + '-' + j + '-' + k, result);
});
}
get charts(): Section[] {
if (this.stakeholder.topics[this.topicIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) {
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) {
return this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].charts;
} else {
return [];
@ -334,8 +340,8 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
get numbers(): Section[] {
if (this.stakeholder.topics[this.topicIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) {
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) {
return this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers;
} else {
return [];
@ -352,9 +358,9 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
get canEdit() {
return this.stakeholder &&
this.stakeholder.topics[this.topicIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex];
this.stakeholder.topics[this.topicIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex] && !this.loading;
}
public get numberIndicatorPaths(): UntypedFormArray {
@ -365,6 +371,14 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
return this.chartIndicatorFb.get('indicatorPaths') as UntypedFormArray;
}
public getActiveIndicatorPath(indicator: Indicator) {
if (indicator.activePath) {
return indicator.indicatorPaths[indicator.activePath];
} else {
return indicator.indicatorPaths[0];
}
}
public getNumberClassBySize(size: IndicatorSize) {
if (size === 'small') {
return 'uk-width-medium@m uk-width-1-1';
@ -461,7 +475,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
}
public getParameters(index: number, type: IndicatorType = 'chart'): UntypedFormArray {
if(type === 'chart') {
if (type === 'chart') {
return this.chartIndicatorPaths.at(index).get('parameters') as UntypedFormArray;
} else {
return this.numberIndicatorPaths.at(index).get('parameters') as UntypedFormArray;
@ -474,7 +488,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
private getSecureUrlByStakeHolder(indicatorPath: IndicatorPath) {
return this.sanitizer.bypassSecurityTrustResourceUrl(
this.indicatorUtils.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)));
this.indicatorUtils.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)));
}
private getUrlByStakeHolder(indicatorPath: IndicatorPath) {
@ -486,71 +500,57 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
jsonPath.push(this.fb.control('', Validators.required));
}
this.numberIndicatorPaths.push(this.fb.group({
url: this.fb.control(url, [Validators.required, StringUtils.urlValidator()]),
jsonPath: jsonPath,
result: this.fb.control(0, Validators.required),
source: this.fb.control(source, Validators.required),
parameters: parameters,
format: this.fb.control(format, Validators.required)
}
url: this.fb.control(url, [Validators.required, StringUtils.urlValidator()]),
jsonPath: jsonPath,
result: this.fb.control(0, Validators.required),
source: this.fb.control(source, Validators.required),
parameters: parameters,
format: this.fb.control(format, Validators.required)
}
));
let index = this.numberIndicatorPaths.length - 1;
if (this.numberIndicatorPaths.at(index).get('url').valid) {
this.validateJsonPath(index);
this.checkForSchemaEnhancements(this.numberIndicatorPaths.at(index).get('url').value);
}
if (this.indicator.defaultId === null) {
this.subscriptions.push(this.numberIndicatorPaths.at(index).get('url').valueChanges.subscribe(value => {
this.numberIndicatorPaths.at(index).get('result').setValue(null);
if (this.numberIndicatorPaths.at(index).get('url').valid) {
let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(value), value, this.stakeholder, this.numberIndicatorPaths.at(index).get('jsonPath').value, this.indicatorUtils.numberSources.get(this.indicatorUtils.getNumberSource(value)));
if (!this.isStakeholderParametersValid(indicatorPath)) {
// default profile
if (this.stakeholder.defaultId == null) {
this.urlParameterizedMessage = "This indicator couldn't be generated properly. Stakeholders based on this profile may not inherit the data correctly."
this.numberIndicatorPaths.at(index).get('result').setValue(null);
if (this.numberIndicatorPaths.at(index).get('url').valid) {
let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(value), value, this.stakeholder, this.numberIndicatorPaths.at(index).get('jsonPath').value, this.indicatorUtils.numberSources.get(this.indicatorUtils.getNumberSource(value)));
if (this.indicator.indicatorPaths[index]) {
this.indicator.indicatorPaths[index] = indicatorPath;
} else {
this.urlParameterizedMessage = "This indicator couldn't be generated properly. Please make sure chart data is for the current stakeholder."
this.indicator.indicatorPaths.push(indicatorPath);
}
} else {
this.urlParameterizedMessage = null;
}
this.checkForSchemaEnhancements(this.numberIndicatorPaths.at(index).get('url').value);
if (this.indicator.indicatorPaths[index]) {
this.indicator.indicatorPaths[index] = indicatorPath;
} else {
this.indicator.indicatorPaths.push(indicatorPath);
}
if (indicatorPath.source) {
this.numberIndicatorPaths.at(index).get('source').setValue(indicatorPath.source);
}
(this.numberIndicatorPaths.at(index) as UntypedFormGroup).setControl('parameters', this.getParametersAsFormArray(indicatorPath));
if (indicatorPath.jsonPath.length > 1 && this.getJsonPath(index).length == 1) {
let paths = indicatorPath.jsonPath;
for (let i = 0; i < paths.length; i++) {
if (i == this.getJsonPath(index).length) {
this.getJsonPath(index).push(this.fb.control('', Validators.required));
if (indicatorPath.source) {
this.numberIndicatorPaths.at(index).get('source').setValue(indicatorPath.source);
}
(this.numberIndicatorPaths.at(index) as UntypedFormGroup).setControl('parameters', this.getParametersAsFormArray(indicatorPath));
if (indicatorPath.jsonPath.length > 1 && this.getJsonPath(index).length == 1) {
let paths = indicatorPath.jsonPath;
for (let i = 0; i < paths.length; i++) {
if (i == this.getJsonPath(index).length) {
this.getJsonPath(index).push(this.fb.control('', Validators.required));
}
}
this.getJsonPath(index).setValue(paths)
}
this.getJsonPath(index).setValue(paths)
}
} else {
this.urlParameterizedMessage = null;
}
})
})
);
this.subscriptions.push(this.numberIndicatorPaths.at(index).get('jsonPath').valueChanges.subscribe(value => {
if (this.indicator.indicatorPaths[index]) {
this.indicator.indicatorPaths[index].jsonPath = value;
}
this.numberIndicatorPaths.at(index).get('result').setValue(null);
})
if (this.indicator.indicatorPaths[index]) {
this.indicator.indicatorPaths[index].jsonPath = value;
}
this.numberIndicatorPaths.at(index).get('result').setValue(null);
})
);
this.subscriptions.push(this.numberIndicatorPaths.at(index).get('source').valueChanges.subscribe(value => {
if (this.indicator.indicatorPaths[index]) {
this.indicator.indicatorPaths[index].source = value;
}
})
if (this.indicator.indicatorPaths[index]) {
this.indicator.indicatorPaths[index].source = value;
}
})
);
} else {
this.numberIndicatorPaths.at(index).get('url').disable();
@ -561,30 +561,18 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
public addChartIndicatorPath(value: string = '', parameters: UntypedFormArray = new UntypedFormArray([]), disableUrl: boolean = false, type: string = null) {
this.chartIndicatorPaths.push(this.fb.group({
url: this.fb.control(value, [Validators.required, StringUtils.urlValidator()]),
parameters: parameters,
type: this.fb.control(type)
}
url: this.fb.control(value, [Validators.required, StringUtils.urlValidator()]),
parameters: parameters,
type: this.fb.control(type)
}
));
let index = this.chartIndicatorPaths.length - 1;
if (disableUrl) {
this.chartIndicatorPaths.at(index).get('url').disable();
} else {
this.checkForSchemaEnhancements(this.chartIndicatorPaths.at(index).get('url').value);
this.urlSubscriptions.push(this.chartIndicatorPaths.at(index).get('url').valueChanges.subscribe(value => {
if (this.chartIndicatorPaths.at(index).get('url').valid) {
let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.indicatorUtils.getChartSource(value), value, this.chartIndicatorPaths.at(index).get('type').value, this.stakeholder);
if (!this.isStakeholderParametersValid(indicatorPath)) {
// default profile
if (this.stakeholder.defaultId == null) {
this.urlParameterizedMessage = "This chart couldn't be generated properly. Stakeholders based on this profile may not inherit the data correctly."
} else {
this.urlParameterizedMessage = "This chart couldn't be generated properly. Please make sure chart data is for the current stakeholder."
}
} else {
this.urlParameterizedMessage = null;
}
this.checkForSchemaEnhancements(this.chartIndicatorPaths.at(index).get('url').value);
(this.chartIndicatorPaths.at(index) as UntypedFormGroup).get('type').setValue(indicatorPath.type);
(this.chartIndicatorPaths.at(index) as UntypedFormGroup).setControl('parameters', this.getParametersAsFormArray(indicatorPath));
if (!this.indicator.indicatorPaths[index]) {
@ -594,17 +582,68 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
indicatorPath.safeResourceUrl = this.indicator.indicatorPaths[index].safeResourceUrl;
this.indicator.indicatorPaths[index] = indicatorPath;
}
} else {
this.urlParameterizedMessage = null;
}
}));
}
}
private isStakeholderParametersValid(indicatorPath: IndicatorPath) {
return !((indicatorPath.chartObject && Object.keys(indicatorPath.parameters).indexOf("index_id") == -1 && Object.keys(indicatorPath.parameters).indexOf("index_name") == -1 && Object.keys(indicatorPath.parameters).indexOf("index_shortName") == -1)
|| (!indicatorPath.chartObject && indicatorPath.url.indexOf("index_id") == -1 && indicatorPath.url.indexOf("index_name") == -1 && (indicatorPath.url).indexOf("index_shortName") == -1));
public removeNumberIndicatorPath(index: number) {
this.numberIndicatorPaths.removeAt(index);
this.indicator.indicatorPaths.splice(index, 1);
this.transitionGroup.init();
if (this.indicator.activePath === index) {
this.activeNumberIndicatorPath(Math.max(0, index - 1));
} else if (this.indicator.activePath > index) {
this.activeNumberIndicatorPath(this.indicator.activePath - 1);
}
this.numberIndicatorFb.markAsDirty();
}
public removeChartIndicatorPath(index: number) {
this.chartIndicatorPaths.removeAt(index);
this.indicator.indicatorPaths.splice(index, 1);
this.transitionGroup.init();
if (this.indicator.activePath === index) {
this.activeChartIndicatorPath(Math.max(0, index - 1));
} else if (this.indicator.activePath > index) {
this.activeChartIndicatorPath(this.indicator.activePath - 1);
}
this.chartIndicatorFb.markAsDirty();
}
public moveIndicatorPath(form: FormGroup,
indicatorPaths: FormArray, index: number,
newIndex: number = index - 1) {
this.transitionGroup.init();
let a = indicatorPaths.at(index);
let b = indicatorPaths.at(newIndex);
indicatorPaths.setControl(index, b);
indicatorPaths.setControl(newIndex, a);
HelperFunctions.swap(this.indicator.indicatorPaths, index, newIndex);
if (this.indicator.activePath === index) {
this.indicator.activePath = newIndex;
} else if (this.indicator.activePath === newIndex) {
this.indicator.activePath = index;
}
form.markAsDirty();
}
public activeNumberIndicatorPath(index: number) {
let paths = this.numberIndicatorPaths;
if (index == paths.length) {
this.addNumberIndicatorPath();
this.transitionGroup.init();
}
this.indicator.activePath = index;
}
public activeChartIndicatorPath(index: number) {
let paths = this.chartIndicatorPaths;
if (index == paths.length) {
this.addChartIndicatorPath();
this.transitionGroup.init();
}
this.indicator.activePath = index;
}
private getJsonPathAsFormArray(indicatorPath: IndicatorPath): UntypedFormArray {
@ -620,6 +659,9 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
private getParametersAsFormArray(indicatorPath: IndicatorPath): UntypedFormArray {
let parameters = this.fb.array([]);
if (indicatorPath.parameters) {
if(!indicatorPath.parameters.statsProfile) {
indicatorPath.parameters.statsProfile = null;
}
Object.keys(indicatorPath.parameters).forEach(key => {
if (this.indicatorUtils.ignoredParameters.indexOf(key) === -1) {
if (this.indicatorUtils.parametersValidators.has(key)) {
@ -640,7 +682,21 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
}
public editNumberIndicatorOpen(section: Section, id = null) {
this.urlParameterizedMessage = null;
this.editNumberModal.cancelButtonText = 'Cancel';
this.editNumberModal.okButtonLeft = false;
this.editNumberModal.alertMessage = false;
if (this.index === -1) {
this.editNumberModal.alertTitle = 'Create a new number indicator';
this.editNumberModal.okButtonText = 'Save';
this.notification = NotificationUtils.createIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
this.editNumberNotify.reset(this.notification.message);
} else {
this.editNumberModal.alertTitle = 'Edit number indicator\'s information';
this.editNumberModal.okButtonText = 'Save Changes';
this.notification = NotificationUtils.editIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
this.editNumberNotify.reset(this.notification.message);
}
this.editNumberModal.stayOpen = true;
this.section = section;
this.index = (id) ? section.indicators.findIndex(value => value._id === id) : -1;
if (this.index !== -1) {
@ -682,26 +738,26 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
this.numberIndicatorFb.get('description').disable();
}, 0);
}
this.editNumberModal.cancelButtonText = 'Cancel';
this.editNumberModal.okButtonLeft = false;
this.editNumberModal.alertMessage = false;
if (this.index === -1) {
this.editNumberModal.alertTitle = 'Create a new number indicator';
this.editNumberModal.okButtonText = 'Save';
this.notification = NotificationUtils.createIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
this.editNumberNotify.reset(this.notification.message);
} else {
this.editNumberModal.alertTitle = 'Edit number indicator\'s information';
this.editNumberModal.okButtonText = 'Save Changes';
this.notification = NotificationUtils.editIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
this.editNumberNotify.reset(this.notification.message);
}
this.editNumberModal.stayOpen = true;
this.editNumberModal.open();
}
public editChartIndicatorOpen(section: Section, id = null) {
this.urlParameterizedMessage = null;
this.editChartModal.cancelButtonText = 'Cancel';
this.editChartModal.okButtonLeft = false;
this.editChartModal.alertMessage = false;
if (this.index === -1) {
this.editChartModal.alertTitle = 'Create a new chart indicator';
this.editChartModal.okButtonText = 'Save';
this.notification = NotificationUtils.createIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
this.editChartNotify.reset(this.notification.message);
} else {
this.editChartModal.alertTitle = 'Edit chart indicator\'s information';
this.editChartModal.okButtonText = 'Save Changes';
this.notification = NotificationUtils.editIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
;
this.editChartNotify.reset(this.notification.message);
}
this.editChartModal.stayOpen = true;
this.urlSubscriptions.forEach(value => {
if (value instanceof Subscriber) {
value.unsubscribe();
@ -725,7 +781,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
});
this.indicator.indicatorPaths.forEach(indicatorPath => {
this.addChartIndicatorPath(this.getUrlByStakeHolder(indicatorPath),
this.getParametersAsFormArray(indicatorPath), this.indicator.defaultId !== null, indicatorPath.type);
this.getParametersAsFormArray(indicatorPath), this.indicator.defaultId !== null, indicatorPath.type);
indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
});
} else {
@ -748,22 +804,6 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
this.chartIndicatorFb.get('description').disable();
}, 0);
}
this.editChartModal.cancelButtonText = 'Cancel';
this.editChartModal.okButtonLeft = false;
this.editChartModal.alertMessage = false;
if (this.index === -1) {
this.editChartModal.alertTitle = 'Create a new chart indicator';
this.editChartModal.okButtonText = 'Save';
this.notification = NotificationUtils.createIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
this.editChartNotify.reset(this.notification.message);
} else {
this.editChartModal.alertTitle = 'Edit chart indicator\'s information';
this.editChartModal.okButtonText = 'Save Changes';
this.notification = NotificationUtils.editIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
;
this.editChartNotify.reset(this.notification.message);
}
this.editChartModal.stayOpen = true;
this.editChartModal.open();
}
@ -863,7 +903,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
this.setCharts();
this.setNumbers();
this.initReorder();
if(properties.notificationsAPIURL) {
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;
@ -900,20 +940,39 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
});
}
}
this.editing = false;
this.importLoading = false;
this.finish();
NotificationHandler.rise('Indicators have been <b>imported</b> successfully!');
}, error => {
this.chartIndicatorFb = null;
NotificationHandler.rise('An error has occurred. Please try again later', 'danger');
this.editing = false;
this.importLoading = false;
this.finish();
}));
}
reorderIndicators(sectionId: string, type: IndicatorType, reorder: Reorder) {
finish() {
this.editing = false;
this.loading = false;
}
moveIndicator(moveIndicator: MoveIndicator) {
this.editing = true;
let path = [
this.stakeholder._id,
this.stakeholder.topics[this.topicIndex]._id,
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id
];
this.subscriptions.push(this.stakeholderService.moveIndicator(this.properties.monitorServiceAPIURL, path, moveIndicator).subscribe(subCategory => {
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex] = subCategory;
this.setCharts();
this.setNumbers();
this.editing = false;
}));
}
reorderIndicators(sectionId: string, type: IndicatorType, indicators: string[]) {
this.editing = true;
let path = [
this.stakeholder._id,
@ -922,7 +981,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
sectionId
];
this.subscriptions.push(this.stakeholderService.reorderIndicators(this.properties.monitorServiceAPIURL, path, reorder, type).subscribe(indicators => {
this.subscriptions.push(this.stakeholderService.reorderIndicators(this.properties.monitorServiceAPIURL, path, indicators).subscribe(indicators => {
if (type === 'chart') {
this.charts.find(section => section._id === sectionId).indicators = indicators;
this.setCharts();
@ -936,7 +995,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
hasDifference(index: number, type: IndicatorType = 'chart'): boolean {
let hasDifference = false;
if(type === 'chart') {
if (type === 'chart') {
this.chartIndicatorPaths.at(index).value.parameters.forEach(parameter => {
if (parameter.value !== this.indicator.indicatorPaths[index].parameters[parameter.key]) {
hasDifference = true;
@ -945,7 +1004,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
});
return hasDifference || this.indicator.indicatorPaths[index].safeResourceUrl.toString() !==
this.getSecureUrlByStakeHolder(this.indicator.indicatorPaths[index]).toString();
} else if(type === 'number') {
} else if (type === 'number') {
let indicatorPath = this.numberIndicatorPaths.at(index).value;
indicatorPath.parameters.forEach(parameter => {
if (parameter.value !== this.indicator.indicatorPaths[index].parameters[parameter.key]) {
@ -966,12 +1025,12 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
}
refreshIndicator(type: IndicatorType = 'chart') {
if(type === 'chart') {
if (type === 'chart') {
this.indicator = this.indicatorUtils.generateIndicatorByForm(this.chartIndicatorFb.value, this.indicator.indicatorPaths, 'chart');
this.indicator.indicatorPaths.forEach(indicatorPath => {
indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
});
} else if(type === 'number') {
} else if (type === 'number') {
this.indicator = this.indicatorUtils.generateIndicatorByForm(this.numberIndicatorFb.value, this.indicator.indicatorPaths, 'number');
this.indicator.indicatorPaths.forEach((indicatorPath, index) => {
this.validateJsonPath(index);
@ -1159,24 +1218,6 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
}));
}
// deleteNumberSectionOpen(section: Section, index: number) {
// this.section = section;
// this.index = index;
// this.deleteNumberSectionModal.alertTitle = 'Delete Section';
// this.deleteNumberSectionModal.cancelButtonText = 'No';
// this.deleteNumberSectionModal.okButtonText = 'Yes';
// this.deleteNumberSectionModal.open();
// }
//
// deleteChartSectionOpen(section: Section, index: number) {
// this.section = section;
// this.index = index;
// this.deleteChartSectionModal.alertTitle = 'Delete Section';
// this.deleteChartSectionModal.cancelButtonText = 'No';
// this.deleteChartSectionModal.okButtonText = 'Yes';
// this.deleteChartSectionModal.open();
// }
deleteSectionOpen(section: Section, index: number, type: IndicatorType, childrenAction: string = null) {
if (!this.editing && !section.defaultId) {
this.sectionTypeToDelete = type;
@ -1233,10 +1274,6 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
}));
}
private checkForSchemaEnhancements(url: string) {
this.showCheckForSchemaEnhancements = this.isAdministrator && url && !this.properties.useOldStatisticsSchema && this.indicatorUtils.checkForSchemaEnhancements(url) && this.properties.dashboard != 'irish';
}
migrateFromOldImportJsonFile(charts) {
// first section contains numbers
// second contains charts
@ -1252,35 +1289,50 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
if (chart['sectionIndex'] == null) {
chart['sectionIndex'] = chart['type'] == 'chart' ? chartsSection : 0;
}
if (chart.url && chart.jsonPath) {
chart.indicatorPaths = [{url: chart.url, jsonPath: chart.jsonPath}];
} else if(chart.url) {
chart.indicatorPaths = [{url: chart.url}];
}
}
return charts;
}
importIndicatorsAndSave(charts: any[]) {
importIndicatorsAndSave(stakeholder: Stakeholder, charts: any[]) {
let sectionsToSave: Section[] = [];
let countIndicators = 0;
if (stakeholder.type !== this.stakeholder.type) {
UIkit.notification("The type of this profile is not the same with the file's one!", {
status: 'warning',
timeout: 6000,
pos: 'bottom-right'
});
this.finish();
return;
}
// name description additionalDescription, height, width, visibility
let noValidParams = 0;
let duplicates = 0;
charts = this.migrateFromOldImportJsonFile(charts);
for (let chart of charts) {
chart.visibility = this.showVisibility?chart.visibility:this.stakeholderUtils.defaultValue(this.stakeholderUtils.visibilities);
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 = [];
sectionsToSave[chart['sectionIndex']] = sectionToSave;
}
let exists = false;
let indicatorPath;
let indicatorPaths: IndicatorPath[] = [];
// validate indicators' schema from file
let invalid_file_message;
if (!chart.type) {
invalid_file_message = "No indicator type is specified. Type should be chart or number.";
} else if (chart.type != "chart" && chart.type != "number") {
invalid_file_message = "Invalid indicator type. Type should be chart or number.";
} else if (chart.type == "number" && !chart.jsonPath) {
} else if (chart.indicatorPaths.length === 0) {
invalid_file_message = "No indicator paths are specified."
} else if (chart.type == "number" && chart.indicatorPaths.filter(path => !path.jsonPath).length > 0) {
invalid_file_message = "No jsonPath is specified for number indicator."
} else if (!chart.url) {
} else if (chart.indicatorPaths.filter(path => !path.url).length > 0) {
invalid_file_message = "No indicator url is specified.";
}
@ -1290,46 +1342,47 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
timeout: 6000,
pos: 'bottom-right'
});
this.editing = false;
this.importLoading = false;
this.finish();
break;
}
if (chart.type == "chart") {
indicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.indicatorUtils.getChartSource(chart.url), chart.url, chart.type, this.stakeholder);
for (let section of this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].charts) {
for (let chart of section.indicators) {
if (JSON.stringify(chart.indicatorPaths[0].chartObject) == JSON.stringify(indicatorPath.chartObject)) {
duplicates++;
exists = true;
}
}
}
indicatorPaths = chart.indicatorPaths.map(path => this.indicatorUtils.generateIndicatorByChartUrl(this.indicatorUtils.getChartSource(path.url), path.url, chart.type, stakeholder));
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].charts.forEach((section: Section) => {
section.indicators.forEach(indicator => {
indicator.indicatorPaths.forEach(path => {
let size = indicatorPaths.length;
indicatorPaths = indicatorPaths.filter(indicatorPath => JSON.stringify(path.chartObject) !== JSON.stringify(indicatorPath.chartObject))
if (indicatorPaths.length < size) {
duplicates = duplicates + (size - indicatorPaths.length);
exists = true;
}
});
});
});
} else if (chart.type == "number") {
indicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(chart.url), chart.url, this.stakeholder,
chart.jsonPath, this.indicatorUtils.numberSources.get(this.indicatorUtils.getNumberSource(chart.url)));
for (let section of this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].numbers) {
for (let chart of section.indicators) {
if (JSON.stringify(chart.indicatorPaths[0].chartObject) == JSON.stringify(indicatorPath.chartObject)) {
duplicates++;
exists = true;
}
}
}
indicatorPaths = chart.indicatorPaths.map(path =>
this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(path.url), path.url,
stakeholder, path.jsonPath, this.indicatorUtils.numberSources.get(this.indicatorUtils.getNumberSource(path.url))));
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].numbers.forEach((section: Section) => {
section.indicators.forEach(indicator => {
indicator.indicatorPaths.forEach(path => {
let size = indicatorPaths.length;
indicatorPaths = indicatorPaths.filter(indicatorPath => JSON.stringify(path.chartObject) !== JSON.stringify(indicatorPath.chartObject))
if (indicatorPaths.length < size) {
duplicates = duplicates + (size - indicatorPaths.length);
exists = true;
}
});
});
});
}
if (!this.isStakeholderParametersValid(indicatorPath)) {
noValidParams++;
}
if (!exists) {
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]);
if (indicatorPaths.length > 0) {
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), indicatorPaths);
sectionsToSave[chart['sectionIndex']].indicators.push(i);
countIndicators++;
}
}
if (duplicates > 0) {
UIkit.notification(duplicates + " urls already exist and will not be imported!", {
status: 'warning',
@ -1337,19 +1390,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
pos: 'bottom-right'
});
}
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.";
}
UIkit.notification(noValidMessage, {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
this.editing = false;
this.importLoading = false;
} else if (sectionsToSave.length > 0 && countIndicators > 0) {
if (sectionsToSave.length > 0 && countIndicators > 0) {
this.saveIndicators(sectionsToSave.filter(section => !!section));
}
if (sectionsToSave.length == 0 || countIndicators == 0) {
@ -1358,8 +1399,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
timeout: 6000,
pos: 'bottom-right'
});
this.editing = false;
this.importLoading = false;
this.finish();
}
}
@ -1370,36 +1410,41 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
let indexIndicator: number = 0;
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[subcategoryIndex].numbers.forEach(section => {
section.indicators.forEach(indicator => {
indicator.indicatorPaths.forEach(indicatorPath => {
indicators[indexIndicator] = {
"type": indicator.type, "name": indicator.name, "jsonPath": indicatorPath.jsonPath,
"description": indicator.description, "additionalDescription": indicator.additionalDescription,
"visibility": indicator.visibility, "width": indicator.width, "height": indicator.height,
"url": this.indicatorUtils.getNumberUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)),
"sectionTitle": section.title,
"sectionType": section.type,
"sectionIndex": index
};
indexIndicator++;
});
indicators[indexIndicator] = {
"indicatorPaths": indicator.indicatorPaths.map(path => {
return {
jsonPath: path.jsonPath,
url: this.indicatorUtils.getNumberUrl(path.source, this.indicatorUtils.getFullUrl(this.stakeholder, path))
}
}),
"type": indicator.type, "name": indicator.name,
"description": indicator.description, "additionalDescription": indicator.additionalDescription,
"visibility": indicator.visibility, "width": indicator.width, "height": indicator.height,
"sectionTitle": section.title,
"sectionType": section.type,
"sectionIndex": index
};
indexIndicator++;
});
index++;
});
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[subcategoryIndex].charts.forEach(section => {
section.indicators.forEach(indicator => {
indicator.indicatorPaths.forEach(indicatorPath => {
indicators[indexIndicator] = {
"type": indicator.type, "name": indicator.name,
"description": indicator.description, "additionalDescription": indicator.additionalDescription,
"visibility": indicator.visibility, "width": indicator.width, "height": indicator.height,
"url": this.getUrlByStakeHolder(indicatorPath),
"sectionTitle": section.title,
"sectionType": section.type,
"sectionIndex": index
};
indexIndicator++;
});
indicators[indexIndicator] = {
"indicatorPaths": indicator.indicatorPaths.map(path => {
return {
url: this.getUrlByStakeHolder(path)
}
}),
"type": indicator.type, "name": indicator.name,
"description": indicator.description, "additionalDescription": indicator.additionalDescription,
"visibility": indicator.visibility, "width": indicator.width, "height": indicator.height,
"sectionTitle": section.title,
"sectionType": section.type,
"sectionIndex": index
};
indexIndicator++;
});
index++;
@ -1408,8 +1453,12 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
let topic = this.stakeholder ? this.stakeholder.topics[this.topicIndex] : null;
let category = topic ? topic.categories[this.categoryIndex] : null;
let subCategory = category ? category.subCategories[subcategoryIndex] : null;
var jsonFileUrl = window.URL.createObjectURL(new Blob([JSON.stringify(indicators)], {type: 'application/json'}));
let json = {
stakeholder: HelperFunctions.copy(this.stakeholder),
indicators: indicators
}
delete json.stakeholder.topics;
var jsonFileUrl = window.URL.createObjectURL(new Blob([JSON.stringify(json)], {type: 'application/json'}));
var a = window.document.createElement('a');
window.document.body.appendChild(a);
a.setAttribute('style', 'display: none');
@ -1418,14 +1467,13 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
a.click();
window.URL.revokeObjectURL(jsonFileUrl);
a.remove(); // remove the element
this.editing = false;
this.finish();
}
fileChangeEvent(fileInput: any, index) {
this.index = index;
this.editing = true;
this.importLoading = true;
this.loading = true;
this.filesToUpload = <Array<File>>fileInput.target.files;
this.upload();
}
@ -1438,8 +1486,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
timeout: 6000,
pos: 'bottom-right'
});
this.editing = false;
this.importLoading = false;
this.finish();
return;
} else {
if (this.filesToUpload[0].name.indexOf(".json") == -1 || (this.filesToUpload[0].type != "application/json")) {
@ -1449,27 +1496,30 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
timeout: 6000,
pos: 'bottom-right'
});
this.editing = false;
this.importLoading = false;
this.finish();
return;
}
}
this.makeFileRequest(this.properties.utilsService + '/upload?type=json', [], this.filesToUpload).then(async (result: string) => {
let json_result = JSON.parse(result);
let json = JSON.parse(result);
// validate file
if (!json_result || json_result.length == 0) {
if (json && Array.isArray(json)) {
UIkit.notification("This file is not supported any more. Please export indicators and try again!", {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
this.finish();
} else if (!json || json?.indicators.length == 0) {
UIkit.notification("Importing file is empty", {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
this.editing = false;
this.importLoading = false;
this.finish();
} else {
this.importIndicatorsAndSave(json_result);
this.importIndicatorsAndSave(json.stakeholder, json.indicators);
}
}, (error) => {
console.error("Error importing files", error);
@ -1478,8 +1528,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
timeout: 6000,
pos: 'bottom-right'
});
this.editing = false;
this.importLoading = false;
this.finish();
});
}

View File

@ -242,30 +242,18 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit,
}
topicChanged(callback: Function, save: boolean = false) {
if(this.topics && save) {
this.topics.disable();
}
if(this.categories) {
this.categories.disable();
}
if(this.subCategories) {
this.subCategories.disable();
}
if(callback) {
callback();
}
this.cdr.detectChanges();
if(this.topics && save) {
this.topics.init();
this.topics.enable();
}
if(this.categories) {
this.categories.init();
this.categories.enable();
}
if(this.subCategories) {
this.subCategories.init();
this.subCategories.enable();
}
}
@ -359,6 +347,8 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit,
this.stakeholder.topics.splice(this.index, 1);
if(this.topicIndex === this.index) {
this.chooseTopic(Math.max(0, this.index - 1));
} else if(this.topicIndex > this.index) {
this.chooseTopic(this.topicIndex - 1);
}
}, true);
};
@ -388,23 +378,15 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit,
}
categoryChanged(callback: Function, save: boolean = false) {
if(this.categories && save) {
this.categories.disable();
}
if(this.subCategories) {
this.subCategories.disable();
}
if(callback) {
callback();
}
this.cdr.detectChanges();
if(this.categories && save) {
this.categories.init();
this.categories.enable();
}
if(this.subCategories) {
this.subCategories.init();
this.subCategories.enable();
}
}
@ -500,6 +482,8 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit,
this.stakeholder.topics[this.topicIndex].categories.splice(this.index, 1);
if(this.categoryIndex === this.index) {
this.chooseCategory(Math.max(0, this.index - 1));
} else if(this.categoryIndex > this.index) {
this.chooseCategory(this.categoryIndex - 1);
}
}, true);
};
@ -528,16 +512,12 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit,
}
subCategoryChanged(callback: Function, save: boolean = false) {
if(this.subCategories && save) {
this.subCategories.disable();
}
if(callback) {
callback();
}
this.cdr.detectChanges();
if(this.subCategories && save) {
this.subCategories.init();
this.subCategories.enable();
}
}
@ -640,6 +620,8 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit,
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories.splice(this.index, 1);
if(this.subCategoryIndex === this.index) {
this.chooseSubcategory(Math.max(0, this.index - 1));
} else if(this.subCategoryIndex > this.index) {
this.chooseSubcategory(this.subCategoryIndex - 1);
}
}, true);
};

View File

@ -20,11 +20,12 @@ import {TransitionGroupModule} from "../../utils/transition-group/transition-gro
import {NumberRoundModule} from "../../utils/pipes/number-round.module";
import {SideBarModule} from "../../dashboard/sharedComponents/sidebar/sideBar.module";
import {SidebarMobileToggleModule} from "../../dashboard/sharedComponents/sidebar/sidebar-mobile-toggle/sidebar-mobile-toggle.module";
import {SliderTabsModule} from "../../sharedComponents/tabs/slider-tabs.module";
@NgModule({
imports: [
CommonModule, TopicRoutingModule, ClickModule, RouterModule, FormsModule, AlertModalModule,
ReactiveFormsModule, InputModule, IconsModule, PageContentModule, LoadingModule, NotifyFormModule, LogoUrlPipeModule, TransitionGroupModule, NumberRoundModule, SideBarModule, SidebarMobileToggleModule
ReactiveFormsModule, InputModule, IconsModule, PageContentModule, LoadingModule, NotifyFormModule, LogoUrlPipeModule, TransitionGroupModule, NumberRoundModule, SideBarModule, SidebarMobileToggleModule, SliderTabsModule
],
declarations: [
TopicComponent, IndicatorsComponent

View File

@ -17,6 +17,11 @@ import {Session} from "../../login/utils/helper.class";
import {HelperFunctions} from "../../utils/HelperFunctions.class";
import {properties} from "src/environments/environment";
class Roles {
manager = 'manager';
member = 'member';
}
class Entities {
stakeholder = 'Dashboard';
funder = 'Funder';
@ -37,6 +42,7 @@ class Entities {
}
export class StakeholderConfiguration {
public static ROLES: Roles = new Roles();
public static ENTITIES: Entities = new Entities();
public static TYPES: Option[] = [
{value: 'funder', label: StakeholderConfiguration.ENTITIES.funder},
@ -55,9 +61,15 @@ export class StakeholderConfiguration {
{icon: 'incognito', value: "PRIVATE", label: 'Private'},
];
public static CACHE_INDICATORS: boolean = true;
public static NUMBER_MULTI_INDICATOR_PATHS = false;
public static CHART_MULTI_INDICATOR_PATHS = true;
}
export class StakeholderUtils {
get roles() {
return StakeholderConfiguration.ROLES;
}
get entities() {
return StakeholderConfiguration.ENTITIES;
}
@ -82,6 +94,14 @@ export class StakeholderUtils {
return StakeholderConfiguration.CACHE_INDICATORS;
}
get hasMultiNumberIndicatorPaths() {
return StakeholderConfiguration.NUMBER_MULTI_INDICATOR_PATHS;
}
get hasMultiChartIndicatorPaths() {
return StakeholderConfiguration.CHART_MULTI_INDICATOR_PATHS;
}
visibilityIcon: Map<Visibility, string> = new Map<Visibility, string>(this.visibilities.map(option => [option.value, option.icon]));
defaultValue(options: Option[]) {

View File

@ -183,6 +183,7 @@ export class Indicator {
visibility: Visibility;
defaultId: string;
indicatorPaths: IndicatorPath[];
activePath: number = 0;
overlay: Overlay = false;
constructor(name: string, description: string, additionalDescription:string, type: IndicatorType, width: IndicatorSize,height: IndicatorSize, visibility: Visibility, indicatorPaths: IndicatorPath[], defaultId: string = null) {

View File

@ -53,7 +53,6 @@ export abstract class MonitorIndicatorStakeholderBaseComponent extends Indicator
minYear = Dates.currentYear - 20;
maxYear = Dates.currentYear;
public numberResults: Map<string, number> = new Map<string, number>();
public chartsActiveType: Map<string, IndicatorPath> = new Map<string, IndicatorPath>();
public clipboard;
/** Services */
@ -214,15 +213,17 @@ export abstract class MonitorIndicatorStakeholderBaseComponent extends Indicator
(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][]>();
let urls: Map<string, [number, number, number][]> = new Map<string, [number, 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);
number.indicatorPaths.forEach((indicatorPath, k) => {
let url = this.getFullUrl(indicatorPath);
const pair = JSON.stringify([indicatorPath.source, url]);
const indexes = urls.get(pair) ? urls.get(pair) : [];
indexes.push([i, j, k]);
urls.set(pair, indexes);
})
}
});
});
@ -231,10 +232,10 @@ export abstract class MonitorIndicatorStakeholderBaseComponent extends Indicator
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]) => {
indexes.forEach(([i, j, k]) => {
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 => {
this.activeSubCategory.numbers[i].indicators[j].indicatorPaths[k].jsonPath.forEach(jsonPath => {
if (result) {
result = result[jsonPath];
}
@ -247,7 +248,7 @@ export abstract class MonitorIndicatorStakeholderBaseComponent extends Indicator
} else {
result = 0;
}
this.numberResults.set(i + '-' + j, result);
this.numberResults.set(i + '-' + j + '-' + k, result);
}
});
}
@ -255,10 +256,9 @@ export abstract class MonitorIndicatorStakeholderBaseComponent extends Indicator
});
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]);
}
indicator.indicatorPaths.forEach((indicatorPath, k) => {
indicator.indicatorPaths[k].safeResourceUrl = this.getUrlByStakeHolder(indicator.indicatorPaths[k]);
});
});
});
if (this.cdr && !(this.cdr as ViewRef).destroyed) {
@ -271,10 +271,12 @@ export abstract class MonitorIndicatorStakeholderBaseComponent extends Indicator
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 getActiveIndicatorPath(indicator: Indicator) {
if(indicator.activePath) {
return indicator.indicatorPaths[indicator.activePath];
} else {
return indicator.indicatorPaths[0];
}
}
public filter() {

View File

@ -1,16 +1,21 @@
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {BehaviorSubject, from, Observable, Subscriber} from "rxjs";
import {Indicator, Section, Stakeholder, StakeholderInfo, Visibility} from "../entities/stakeholder";
import {Indicator, Section, Stakeholder, StakeholderInfo, SubCategory, Visibility} from "../entities/stakeholder";
import {HelperFunctions} from "../../utils/HelperFunctions.class";
import {map} from "rxjs/operators";
import {properties} from "../../../../environments/environment";
import {CustomOptions} from "../../services/servicesUtils/customOptions.class";
export interface Reorder {
action: 'moved' | 'added' | 'removed',
target: string,
ids: string[];
export interface SectionInfo {
id: string;
indicators: string[];
}
export interface MoveIndicator {
target: string;
from: SectionInfo;
to: SectionInfo;
}
@Injectable({
@ -128,7 +133,6 @@ export class StakeholderService {
if (element.alias && element.alias.startsWith('/')) {
element.alias = element.alias.slice(1);
}
path = HelperFunctions.encodeArray(path);
return this.http.post<any>(url + ((path.length > 0) ? '/' : '') + path.join('/') +
'/save', element, CustomOptions.registryOptions()).pipe(map(element => {
if (path.length === 0) {
@ -140,7 +144,6 @@ export class StakeholderService {
}
saveBulkElements(url: string, indicators, path: string[] = []): Observable<any> {
path = HelperFunctions.encodeArray(path);
return this.http.post<any>(url + ((path.length > 0) ? '/' : '') + path.join('/') +
'/save-bulk', indicators, CustomOptions.registryOptions()).pipe(map(element => {
if (path.length === 0) {
@ -152,7 +155,6 @@ export class StakeholderService {
}
saveSection(url: string, element: any, path: string[] = [], index: number = -1): Observable<Section> {
path = HelperFunctions.encodeArray(path);
return this.http.post<Section>(url + ((path.length > 0) ? '/' : '') + path.join('/') +
'/save/' + index, element, CustomOptions.registryOptions()).pipe(map(element => {
return HelperFunctions.copy(element);
@ -160,7 +162,6 @@ export class StakeholderService {
}
deleteElement(url: string, path: string[], childrenAction: string = null): Observable<any> {
path = HelperFunctions.encodeArray(path);
let params: string = "";
if (childrenAction) {
params = "?children=" + childrenAction;
@ -169,17 +170,21 @@ export class StakeholderService {
}
reorderElements(url: string, path: string[], ids: string[]): Observable<any> {
path = HelperFunctions.encodeArray(path);
return this.http.post<any>(url + '/' + path.join('/') + '/reorder', ids, CustomOptions.registryOptions());
}
reorderIndicators(url: string, path: string[], reorder: Reorder, type: string = 'chart'): Observable<Indicator[]> {
path = HelperFunctions.encodeArray(path);
return this.http.post<Indicator[]>(url + '/' + path.join('/') + '/' + type + '/reorder', reorder, CustomOptions.registryOptions()).pipe(map(indicators => {
reorderIndicators(url: string, path: string[], indicators: string[]): Observable<Indicator[]> {
return this.http.post<Indicator[]>(url + '/' + path.join('/') + '/reorder', indicators, CustomOptions.registryOptions()).pipe(map(indicators => {
return HelperFunctions.copy(indicators);
}));
}
moveIndicator(url: string, path: string[], moveIndicator: MoveIndicator): Observable<SubCategory> {
return this.http.post<SubCategory>(url + '/' + path.join('/') + '/moveIndicator', moveIndicator, CustomOptions.registryOptions()).pipe(map(subCategory => {
return HelperFunctions.copy(subCategory);
}));
}
getStakeholderAsObservable(): Observable<Stakeholder> {
return this.stakeholderSubject.asObservable();
}

View File

@ -12,6 +12,8 @@ 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";
import {StakeholderUtils} from "../monitor-admin/utils/indicator-utils";
import {StringUtils} from "../utils/string-utils.class";
@Component({
selector: 'role-verification',
@ -19,12 +21,9 @@ import {BaseComponent} from "../sharedComponents/base/base.component";
<modal-alert #managerModal [overflowBody]="false" (alertOutput)="verifyManager()" (cancelOutput)="cancel()"
[okDisabled]="code.invalid || loading">
<div>
You have been invited to join <span
class="uk-text-bold">{{name}}</span> {{(service === 'monitor' ? 'Monitor' : 'Research Community')}} Dashboard
as a manager.
You have been invited to join <span class="uk-text-bold">{{name}}</span> {{(dashboard)}} Dashboard as a {{stakeholderUtils.roles.manager}}.
<span class="uk-text-primary">Fill</span> in the <span class="uk-text-primary">verification code</span>, sent to
your
email, to accept the invitation request.
your email, to accept the invitation request.
</div>
<div *ngIf="!loading" class="uk-margin-medium-top uk-flex uk-flex-center">
<div input [formInput]="code" class="uk-width-medium" placeholder="Verification code">
@ -36,14 +35,12 @@ import {BaseComponent} from "../sharedComponents/base/base.component";
</div>
</modal-alert>
<modal-alert #memberModal [overflowBody]="false" (cancelOutput)="cancel()"
(alertOutput)="verifyMember()" [okDisabled]="(code.invalid || loading) && !isMember">
<div *ngIf="!isMember">
(alertOutput)="verifyMember()" [okDisabled]="(code.invalid || loading)">
<div>
<div>
You have been invited to join <span class="uk-text-bold">{{name}}</span> Monitor Dashboard as a member.
You have been invited to join <span class="uk-text-bold">{{name}}</span> {{(dashboard)}} Dashboard as a {{stakeholderUtils.roles.member}}.
<span class="uk-text-primary">Fill</span> in the <span class="uk-text-primary">verification code</span>, sent
to
your
email, to accept the invitation request.
to your email, to accept the invitation request.
</div>
<div *ngIf="!loading" class="uk-margin-medium-top uk-flex uk-flex-wrap uk-flex-center">
<div input [formInput]="code" class="uk-width-medium" placeholder="Verification code">
@ -54,13 +51,6 @@ import {BaseComponent} from "../sharedComponents/base/base.component";
<loading></loading>
</div>
</div>
<div *ngIf="isMember">
<div>
Welcome! You are now a member of the OpenAIRE Monitor Dashboard for the <span
class="uk-text-bold">{{name}}</span>!
From now on, you will have access to our restricted content.
</div>
</div>
</modal-alert>
<modal-alert #errorModal (alertOutput)="cancel()" [overflowBody]="false">
<div>
@ -95,6 +85,8 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
public userInfoLink = null;
@Input()
public relativeTo: ActivatedRoute = this._route;
@Input()
public dashboard: string = 'Research Community';
public user: User;
public verification: any;
public code: UntypedFormControl;
@ -105,8 +97,8 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
@ViewChild('errorModal') errorModal: AlertModal;
public error: string = null;
public loading: boolean = false;
public isMember: boolean = false;
public stakeholderUtils: StakeholderUtils = new StakeholderUtils();
constructor(protected _route: ActivatedRoute,
protected _router: Router,
private fb: UntypedFormBuilder,
@ -117,18 +109,23 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
private cdr: ChangeDetectorRef) {
super();
}
ngOnInit() {
this.reset();
}
ngAfterViewInit() {
this.init();
}
init() {
this.ngOnDestroy();
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
this.user = user;
this.paramsSubscription = this._route.queryParams.subscribe(params => {
if (params) {
this.cdr.detectChanges();
if(params['verify'] && !this.isMember) {
if(params['verify']) {
if (this.user) {
this.subscriptions.push(this.userRegistryService.getInvitation(params['verify']).subscribe(verification => {
this.verification = verification;
@ -155,51 +152,43 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
relativeTo: this.relativeTo
});
}
} else if(this.isMember) {
this.openMemberModal();
}
} else {
this.isMember = false;
this.cdr.detectChanges();
}
});
}));
}
ngOnDestroy() {
super.ngOnDestroy();
if (this.paramsSubscription instanceof Subscription) {
this.paramsSubscription.unsubscribe();
}
}
public openManagerModal() {
this.error = null;
this.managerModal.okButtonLeft = false;
this.managerModal.okButtonText = 'Accept';
this.managerModal.stayOpen = true;
this.managerModal.cancelButtonText = 'Cancel';
this.managerModal.alertTitle = 'Manager Invitation';
this.managerModal.alertTitle = StringUtils.capitalize(this.stakeholderUtils.roles.manager) + ' Invitation';
this.managerModal.open();
}
public openMemberModal() {
this.error = null;
if(this.isMember) {
this.memberModal.cancelButton = false;
this.memberModal.okButtonText = 'Close';
} else {
this.memberModal.cancelButton = true;
this.memberModal.okButtonText = 'Accept';
}
this.memberModal.cancelButton = true;
this.memberModal.okButtonText = 'Accept';
this.memberModal.okButtonLeft = false;
this.memberModal.stayOpen = true;
this.memberModal.cancelButtonText = 'Cancel';
this.memberModal.alertTitle = 'Member Invitation';
this.memberModal.alertTitle = StringUtils.capitalize(this.stakeholderUtils.roles.member) + ' Invitation';
this.cdr.detectChanges();
this.memberModal.open();
}
public openErrorModal() {
this.error = null;
this.errorModal.cancelButton = false;
@ -207,79 +196,61 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
this.errorModal.alertTitle = 'Invalid URL';
this.errorModal.open();
}
public verifyManager() {
this.loading = true;
this.subscriptions.push(this.userRegistryService.verify(this.verification.id, this.code.value).subscribe(() => {
// this.clearCacheService.clearCache('Managers updated');
this.managerModal.cancel();
this.error = null;
this.userManagementService.updateUserInfo(() => {
if (this.paramsSubscription instanceof Subscription) {
this.paramsSubscription.unsubscribe();
}
if(this.service === "irish") {
if(this.service === "irish" || this.service === "monitor") {
this.loading = false;
this.userManagementService.login(properties.domain + '/admin/' + this.verification.entity);
} else {
this.subscriptions.push(this.emailService.notifyManagers(this.id, 'manager',
Composer.composeEmailToInformOldManagersForTheNewOnes(this.name, this.id)).subscribe(() => {
this.subscriptions.push(this.emailService.notifyNewManager(Composer.composeEmailForNewManager(this.id, this.name)).subscribe(
() => {
this.loading = false;
window.location.href = properties.adminPortalURL + '/' + this.verification.entity;
},
error1 => {
console.error(error1);
this.loading = false;
window.location.href = properties.adminPortalURL + '/' + this.verification.entity;
}
));
}, error => {
console.error(error);
this.loading = false;
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(() => {
this.subscriptions.push(this.emailService.notifyNewManager(Composer.composeEmailForNewManager(this.id, this.name)).subscribe(
() => {
this.loading = false;
window.location.href = properties.adminPortalURL + '/' + this.verification.entity;
},
error1 => {
console.error(error1);
this.loading = false;
window.location.href = properties.adminPortalURL + '/' + this.verification.entity;
}
));
}, error => {
console.error(error);
this.loading = false;
window.location.href = properties.adminPortalURL + '/' + this.verification.entity;
}));
}
});
window.location.href = properties.adminPortalURL + '/' + this.verification.entity;
}));
}
}, error => {
this.loading = false;
this.error = 'The verification code is invalid';
}));
}
public verifyMember() {
this.loading = true;
if (!this.isMember) {
this.subscriptions.push(this.userRegistryService.verify(this.verification.id, this.code.value, "member").subscribe(() => {
// this.clearCacheService.clearCache('Members updated');
this.loading = false;
this.error = null;
this.isMember = true;
this.userManagementService.updateUserInfo(() => {
if (this.paramsSubscription instanceof Subscription) {
this.paramsSubscription.unsubscribe();
}
this.cancel();
});
}, error => {
this.loading = false;
this.error = 'The verification code is invalid';
}));
} else {
this.subscriptions.push(this.userRegistryService.verify(this.verification.id, this.code.value, "member").subscribe(() => {
// this.clearCacheService.clearCache('Members updated');
this.memberModal.cancel();
}
this.loading = false;
this.error = null;
window.location.href = window.location.href.split('?')[0];
}, error => {
this.loading = false;
this.error = 'The verification code is invalid';
}));
}
public reset() {
this.code = this.fb.control('', [Validators.required, Validators.pattern('^[+0-9]{6}$')]);
}
cancel() {
this.isMember = false;
this._router.navigate([]);
}
}

View File

@ -1,11 +1,11 @@
<ng-template #selected_filters_pills>
<h1 class="uk-margin-remove">
<div [class.uk-invisible]="list.children.length === 0">
<div [class.uk-invisible]="list.children.length === 0" [class.uk-margin-top]="list.children.length > 0">
<ul #list class="uk-grid uk-grid-small uk-flex-wrap" uk-grid>
<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-small uk-label-secondary uk-text-truncate target1">
<span class="uk-text-capitalize uk-label uk-label-small uk-label-secondary uk-text-truncate target1">
{{customFilter.valueName}}
</span>
</li>
@ -15,7 +15,7 @@
<ng-container *ngFor="let type of resultTypes.values; let i = index;">
<ng-container *ngIf="type.selected">
<li class="">
<span class="uk-label uk-label-small uk-label-primary uk-flex uk-flex-middle target2">
<span class="uk-text-capitalize uk-label uk-label-small uk-label-primary uk-flex uk-flex-middle target2">
<span class="uk-margin-small-right uk-width-expand uk-text-truncate">{{type.name}}</span>
<button [class.uk-disabled]="disabled" (click)="removeResultType(type.id)" class="uk-close uk-icon" [disabled]="disabled">
<icon name="close" flex="true" ratio="0.7"></icon>
@ -29,7 +29,7 @@
<ng-container *ngFor="let filter of rangeFilters ">
<ng-container *ngIf="filter.selectedFromAndToValues">
<li class="">
<span class="uk-label uk-label-small uk-label-primary uk-flex uk-flex-middle target3">
<span class="uk-text-capitalize uk-label uk-label-small uk-label-primary uk-flex uk-flex-middle target3">
<span class="uk-margin-small-right uk-width-expand uk-text-truncate">{{filter.selectedFromAndToValues}}</span>
<button [class.uk-disabled]="disabled" (click)="removeRangeFilter(filter)" class="uk-close uk-icon" [disabled]="disabled">
<icon name="close" flex="true" ratio="0.7"></icon>
@ -44,7 +44,7 @@
<ng-container *ngFor="let value of getSelectedValues(filter); let i = index; let end = last; ">
<li *ngIf="!customFilter || (customFilter.isHiddenFilter && customFilter.valueId != value.id)"
class="">
<span class="uk-label uk-label-small uk-label-primary uk-flex uk-flex-middle target4">
<span class="uk-text-capitalize uk-label uk-label-small uk-label-primary uk-flex uk-flex-middle target4">
<span
class="uk-margin-small-right uk-width-expand uk-text-truncate">
<ng-container *ngIf="filter.type && (filter.type == 'boolean' || filter.type == 'triplet') else noboolean">
@ -73,7 +73,7 @@
<ng-container *ngIf="filter.countSelectedValues > 0">
<ng-container *ngFor="let value of getSelectedValues(filter); let i = index; let end = last; ">
<li *ngIf="!customFilters || (customFilters[0].isHiddenFilter && customFilters[0].valueId != value.id)">
<span class="uk-label uk-label-small uk-label-primary uk-flex uk-flex-middle target5">
<span class="uk-text-capitalize uk-label uk-label-small uk-label-primary uk-flex uk-flex-middle target5">
<span class="uk-margin-small-right uk-width-expand uk-text-truncate">
<ng-container *ngIf="filter.type && (filter.type == 'boolean' || filter.type == 'triplet') else noboolean">
<span>{{filter.title}}:
@ -288,7 +288,9 @@
</div>
<div class="uk-container uk-margin-top" [class.uk-container-large]="!mobile" [class.uk-container-expand]="mobile" [class.uk-padding-remove-horizontal]="mobile">
<div class="uk-grid uk-margin-large-bottom" [class.uk-margin-top]="!mobile" uk-grid>
<div *ngIf="!mobile && showRefine && (results.length > 0 || (searchUtils.refineStatus == errorCodes.LOADING && searchUtils.status != errorCodes.LOADING) || (!hideFilters &&
<div *ngIf="!mobile && showRefine && (searchUtils.refineStatus == errorCodes.DONE && ((orderedFilters && orderedFilters.length > 0) || (staticFilters && staticFilters.length > 0)
|| (rangeFilters && rangeFilters.length > 0) || (filters && filters.length > 0)))
&& (results.length > 0 || (searchUtils.refineStatus == errorCodes.LOADING && searchUtils.status != errorCodes.LOADING) || (!hideFilters &&
(existingFiltersWithValues > 0 || (selectedRangeFilters + selectedFilters + selectedTypesNum) > 0))) "
class="uk-width-1-4@m search-filters">
<ng-container *ngTemplateOutlet="filters_column; context: {}"></ng-container>
@ -308,31 +310,47 @@
[href]="openaireLink+this.routerHelper.createQueryParamsString(this.parameterNames, this.parameterValues)"
target="_blank"> OpenAIRE - Explore</a>.
</div>
<ng-container *ngIf="mobile">
<div class="uk-flex uk-flex-middle uk-flex-wrap uk-child-width-auto uk-flex-between">
<!-- Total results, number of pages -->
<div class="uk-margin-remove-bottom uk-text-truncate" [class.uk-margin-medium-right]="!mobile" [class.uk-margin-right]="mobile" [class.uk-h6]="!mobile">
<ng-container *ngIf="results && searchUtils.totalResults > 0">
<span>{{searchUtils.totalResults|number}}</span>
<span class="uk-text-meta uk-text-capitalize"> {{type}}</span>
<ng-container *ngIf="searchTerm && simpleView">
<span class="uk-text-meta"> for </span>
<span class="uk-text-bold">{{searchTerm}}</span>
</ng-container>
<ng-container *ngIf="!simpleView && advancedSearchTerms > 0">
<span class="uk-text-bold"> ({{advancedSearchTerms}} rule{{advancedSearchTerms == 1 ? '' : 's'}} applied)</span>
</ng-container>
</ng-container>
<ng-container *ngIf="!loadPaging && oldTotalResults > 0 && searchUtils.status == errorCodes.LOADING">
<span>{{oldTotalResults|number}}</span>
<span class="uk-text-meta uk-text-capitalize"> {{type}}, page </span>
<span>{{searchUtils.page | number}}</span>
<span class="uk-text-meta"> of {{(totalPages(oldTotalResults)|number)}}</span>
</ng-container>
</div>
<!-- Download results -->
<div *ngIf="showDownload && (searchUtils.status !== errorCodes.LOADING || !loadPaging)"
class="uk-margin-small-bottom uk-flex uk-flex-center">
<div *ngIf="mobile && showDownload && (searchUtils.status !== errorCodes.LOADING || !loadPaging)"
class="uk-flex uk-flex-center">
<search-download
*ngIf="( entityType !='community' && entityType != 'stakeholder') && usedBy == 'search'"
[isDisabled]="disabled"
[type]="csvPath" [csvParams]="csvParams" [totalResults]="searchUtils.totalResults">
*ngIf="( entityType !='community' && entityType != 'stakeholder') && usedBy == 'search'"
[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>
<img src="assets/common-assets/common/zenodoDump.png" alt="Zenodo dump" width="20"><span class="uk-margin-xsmall-left">Data dump</span>
</a>
</ng-container>
</div>
</ng-container>
<div *ngIf="(searchUtils.status !== errorCodes.LOADING || !loadPaging) && !mobile" class="uk-flex uk-flex-top">
<!-- filters pills -->
<div class="uk-width-expand@m">
<ng-container *ngTemplateOutlet="selected_filters_pills;"></ng-container>
</div>
<div class="uk-width-auto@m uk-margin-medium-left uk-flex uk-flex-middle">
<div *ngIf="!mobile" class="uk-margin-medium-left uk-flex uk-flex-middle">
<div *ngIf="searchUtils.totalResults > 10 || sort || searchUtils.totalResults > searchUtils.size ||
(!loadPaging && oldTotalResults > searchUtils.size && searchUtils.status == errorCodes.LOADING)"
class="uk-grid uk-flex-middle uk-child-width-1-1 uk-child-width-1-2@m" uk-grid>
class="uk-grid uk-flex-middle uk-child-width-1-1 uk-child-width-1-2@m" uk-grid>
<div>
<div class="uk-grid uk-flex-middle uk-grid-column-collapse" uk-grid>
<search-sorting *ngIf="sort && searchUtils.totalResults > 0"
@ -345,20 +363,27 @@
</div>
<!-- Download results -->
<div *ngIf="showDownload && (searchUtils.status !== errorCodes.LOADING || !loadPaging)"
class="uk-margin-small-left uk-flex uk-flex-middle" [class.uk-flex-center]="mobile" [class.uk-margin-medium-top]="mobile">
class="uk-margin-small-left uk-flex uk-flex-middle" [class.uk-flex-center]="mobile" [class.uk-margin-medium-top]="mobile">
<search-download
*ngIf="( entityType !='community' && entityType != 'stakeholder') && usedBy == 'search'"
[isDisabled]="disabled"
[type]="csvPath" [csvParams]="csvParams" [totalResults]="searchUtils.totalResults">
*ngIf="( entityType !='community' && entityType != 'stakeholder') && usedBy == 'search'"
[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>
<img src="assets/common-assets/common/zenodoDump.png" alt="Zenodo dump" width="20"><span class="uk-margin-xsmall-left">Data dump</span>
</a>
</ng-container>
</div>
</div>
</div>
<div *ngIf="(searchUtils.status !== errorCodes.LOADING || !loadPaging) && !mobile" class="uk-flex uk-flex-top">
<!-- filters pills -->
<div class="uk-width-expand@m">
<ng-container *ngTemplateOutlet="selected_filters_pills;"></ng-container>
</div>
</div>
<div [class]="searchUtils.page > pagingLimit ? 'search-results' : ''"
*ngIf="(searchUtils.page >= pagingLimit) && (searchUtils.totalResults > searchUtils.size*pagingLimit)">
<p class="uk-alert uk-alert-warning" uk-alert>For more results please try a new, more specific query</p>

View File

@ -1085,7 +1085,7 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
let params = "";
let doisParams = "";
var DOIs: Identifier[] = Identifier.getIdentifiersFromString(value);
if ((entityType == 'publication' || entityType == 'dataset' || entityType == 'software' || entityType == 'other' || entityType == "result" || entityType == "dataprovider" || entityType == "service")) {
if ((entityType == 'publication' || entityType == 'dataset' || entityType == 'software' || entityType == 'other' || entityType == "result" || entityType == "dataprovider" || entityType == "service" || entityType == "organization")) {
for (let identifier of DOIs) {
// console.log(identifier)
// pidclassid exact \"doi\" and pid exact \"10.1016/j.nima.2015.11.134\"

View File

@ -57,7 +57,7 @@ export class SearchResultComponent implements OnInit, OnChanges {
this.previewResults.push(this.getResultPreview(result));
}
if ((properties.adminToolsPortalType == "explore" || properties.adminToolsPortalType == "community" || properties.adminToolsPortalType == "aggregator")
if ((properties.adminToolsPortalType == "explore" || properties.adminToolsPortalType == "community" || properties.adminToolsPortalType == "aggregator" || properties.dashboard == "irish")
&& Session.isLoggedIn() && this.results && this.results.length > 0
&& (this.type == "result" || this.type == "publication" || this.type == "dataset" || this.type == "software" || this.type == "other")
) {

View File

@ -2,6 +2,7 @@ import {Email} from "./email";
import {Body} from "./body";
import {properties} from "../../../../environments/environment";
import {User} from "../../login/utils/helper.class";
import {StakeholderConfiguration} from "../../monitor-admin/utils/indicator-utils";
export class Composer {
private static noteBodySize = "14px";
@ -335,13 +336,13 @@ export class Composer {
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>You have been invited to be a ' + StakeholderConfiguration.ROLES[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>As a ' + StakeholderConfiguration.ROLES[role] + ' 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 ' + StakeholderConfiguration.ROLES['member'] + 's.</p>':
'<p>As a ' + StakeholderConfiguration.ROLES[role] + ' of the National Open Access Monitor, Ireland, you will have access to the sandbox 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>' +

View File

@ -23,7 +23,7 @@ export let common: EnvProperties = {
rorURL: "https://ror.org/",
isniURL: "https://isni.org/isni/",
wikiDataURL: "https://www.wikidata.org/wiki/",
fundRefURL: "https://api.crossref.org/funders/",
fundRefURL: "https://data.crossref.org/fundingdata/funder/",
fairSharingURL: "https://fairsharing.org/",
eoscMarketplaceURL: "https://marketplace.eosc-portal.eu/services/",
sherpaURL: "http://sherpa.ac.uk/romeo/issn/",

View File

@ -805,8 +805,8 @@ export class SearchFieldsBase {
//ORGANIZATION
public ORGANIZATION_REFINE_FIELDS: string[] = ["country"]
public ORGANIZATION_ADVANCED_FIELDS: string[] = ["q", "organizationlegalname", "organizationlegalshortname", "country"];
public ORGANIZATION_REFINE_FIELDS: string[] = ["countrynojurisdiction"]
public ORGANIZATION_ADVANCED_FIELDS: string[] = ["q", "organizationlegalname", "organizationlegalshortname", "countrynojurisdiction", "pid"];
public ORGANIZATION_FIELDS: { [key: string]: FieldDetails } = {
["q"]: {name: "Any field", type: "keyword", param: "q", operator: "op", equalityOperator: "=", filterType: null},
@ -826,7 +826,7 @@ export class SearchFieldsBase {
equalityOperator: "=",
filterType: null
},
["country"]: {
["countrynojurisdiction"]: {
name: "Country",
type: "vocabulary",
param: "country",
@ -834,6 +834,7 @@ export class SearchFieldsBase {
equalityOperator: " exact ",
filterType: "checkbox"
},
["pid"]: {name: "PID", type: "keyword", param: "pid", operator: "pd", equalityOperator: " exact ", filterType: null}
};
// public ORGANIZATION_INDEX:string[] = ["organizationcountryname"]//,"organizationeclegalbody"];
// public ADVANCED_SEARCH_ORGANIZATION_PARAM:string[] = ["q","contenttype","compatibility","country","type"];
@ -969,7 +970,7 @@ export class SearchFieldsBase {
return "and";
} else if (fieldId == "instancetypename" || fieldId == "eoscdatasourcetype"
|| fieldId == "resultlanguagename" || fieldId == "datasourceodlanguages"
|| fieldId == "datasourcecompatibilityname" || fieldId == "country" || fieldId == "datasourceodcontenttypes"
|| fieldId == "datasourcecompatibilityname" || fieldId == "country" || fieldId == "countrynojurisdiction" || fieldId == "datasourceodcontenttypes"
|| fieldId == "resulthostingdatasource" || fieldId == "collectedfrom") {
return "or";
}

View File

@ -150,7 +150,7 @@ export class DOI {
}
export class Identifier {
class: "doi" | "pmc" | "pmid" | "handle" | "ORCID" | "re3data" | "swhid" = null;
class: "doi" | "pmc" | "pmid" | "handle" | "ORCID" | "re3data" | "swhid" | "ror" | "wikidata" | "fundref" | "isni" = null;
id: string;
public static getDOIsFromString(str: string): string[] {
@ -205,14 +205,22 @@ export class Identifier {
return {"class": "re3data", "id": pid};
} else if (Identifier.isValidSwhId(pid)) {
return {"class": "swhid", "id": pid};
} else if (Identifier.isValidRor(pid)) {
return {"class": "ror", "id": pid};
} else if (Identifier.isValidFundRef(pid)) {
return {"class": "fundref", "id": pid};
} else if (Identifier.isValidWikidata(pid)) {
return {"class": "wikidata", "id": pid};
} else if (Identifier.isValidIsni(pid)) {
return {"class": "isni", "id": pid};
}
//set it as a doi, to catch the case that doi has not valid format
return (strict?null:{"class": "doi", "id": pid});
}
public static getPIDFromIdentifiers(identifiers: Map<string, string[]>): Identifier {
let classes:string [] = ["doi", "handle", "pmc", "pmid", "re3data", "swhid"];
if(identifiers) {
let classes:string [] = ["doi", "handle", "pmc", "pmid", "re3data", "swhid", "ror", "wikidata", "fundref", "isni"];
if(identifiers && identifiers.size > 0) {
for (let cl of classes) {
if (identifiers.get(cl)) {
for (let pid of identifiers.get(cl)) {
@ -258,6 +266,7 @@ export class Identifier {
}
public static isValidHANDLE(str: string): boolean {
// let exp = /\b[0-9a-zA-Z-]*\/[0-9a-zA-Z-]*$/g; // resolve with url - need to add method for raw value
let exp = /^[0-9a-zA-Z-]*\/[0-9a-zA-Z-]*$/g;
return str.match(exp) != null;
}
@ -272,6 +281,28 @@ export class Identifier {
let exp = /swh:1:snp:[0-9a-f]{40}/g;
return str.match(exp) != null;
}
public static isValidRor(str: string): boolean {
let exp = /0[a-z|0-9]{6}[0-9]{2}\b/g;
return str.match(exp) != null;
}
public static isValidIsni(str: string): boolean {
let exp = /^[0]{7}[0-9]{8}[0-9X]$/g;
return str.match(exp) != null;
}
public static isValidWikidata(str: string): boolean {
let exp = /^Q\d+$/g;
return str.match(exp) != null;
}
public static isValidFundRef(str: string): boolean {
// let exp = /aaa/g;
let exp = /^[1-9]\d+$/g;
return str.match(exp) != null;
}
}
export class StringUtils {

View File

@ -1,6 +1,6 @@
import {TransitionGroupItemDirective} from "./transition-group-item.directive";
import {
AfterViewInit,
AfterViewInit, ChangeDetectorRef,
Component,
ContentChildren,
ElementRef, Input,
@ -29,15 +29,14 @@ export class TransitionGroupComponent implements AfterViewInit, OnDestroy {
@ContentChildren(TransitionGroupItemDirective) items: QueryList<TransitionGroupItemDirective>;
@Input()
public id: string;
private disabled: boolean = false;
public size: number;
private subscription: Subscription;
constructor(public element: ElementRef) {}
ngAfterViewInit() {
this.init();
this.subscription = this.items.changes.subscribe(items => {
if(!this.disabled) {
if(items.length === this.size) {
items.forEach(item => item.prevPos = item.newPos || item.prevPos);
items.forEach(this.runCallback);
this.refreshPosition('newPos');
@ -80,6 +79,7 @@ export class TransitionGroupComponent implements AfterViewInit, OnDestroy {
init() {
this.refreshPosition('prevPos');
this.refreshPosition('newPos');
this.size = this.items.length;
}
runCallback(item: TransitionGroupItemDirective) {
@ -126,17 +126,17 @@ export class TransitionGroupComponent implements AfterViewInit, OnDestroy {
/**
* Enable transition
*
* @deprecated
* */
enable() {
this.disabled = false;
console.debug('Deprecated')
}
/**
* Disable transition
*
* @deprecated
* */
disable() {
this.disabled = true;
console.debug('Deprecated')
}
}