Merge pull request 'Production release March 2024 [Monitor Dashboard]' (#38) from develop into master

Reviewed-on: #38
This commit is contained in:
Konstantinos Triantafyllou 2024-04-01 18:02:04 +02:00
commit 44e821b1f1
20 changed files with 302 additions and 270 deletions

View File

@ -56,25 +56,44 @@
<div *ngIf="claims && claims.length == 0" class="uk-card uk-card-default uk-padding-large uk-text-center uk-margin-bottom uk-text-bold">
<div>No links found</div>
</div>
<ul class="uk-margin-small-top uk-list uk-list-xlarge">
<li *ngFor="let claim of claims; let i=index" class="uk-card uk-card-default">
<div class="uk-card-body">
<div class="uk-grid uk-grid-small" uk-grid>
<ng-container *ngIf="claims && claims.length > 0">
<div class="uk-flex uk-flex-middle uk-margin-top uk-margin-small-bottom uk-padding-small uk-padding-remove-horizontal">
<div class="uk-width-xsmall uk-flex uk-flex-center uk-flex-middle">
<label>
<input id="checkAll" type="checkbox" (click)="selectAll($event)" class="uk-checkbox" title="Select All"
[ngModel]="selected.length == claims.length"/>
</label>
</div>
<button class="uk-button uk-button-link" [class.uk-disabled]="selected.length == 0" [disabled]="selected.length == 0"
(click)="deleteOpen()">
<span>Delete ({{selected.length}})</span>
</button>
</div>
<ul class="uk-margin-small-top uk-list uk-list-striped">
<li *ngFor="let claim of claims; let i=index" class="uk-flex uk-flex-middle uk-padding-small uk-padding-remove-horizontal">
<div class="uk-width-xsmall uk-flex uk-flex-center uk-flex-middle">
<input type="checkbox" class="uk-checkbox"
[id]="claim.id" (click)="selectClaim(claim, $event)" [ngModel]="isSelectedClaim(claim.id)">
</div>
<div class="uk-width-expand">
<div class="uk-margin-bottom">
<div class="uk-grid uk-grid-small uk-flex-middle" uk-grid>
<div class="uk-width-expand">
<div class="uk-margin-small-bottom">
<claim-entity [entity]="claim.target" [type]="claim.targetType" [properties]=properties
[externalPortalUrl]=externalPortalUrl [source]="true" [linkAvailable]="isClaimAvailable(claim)"></claim-entity>
</div>
<div class="uk-margin-bottom">
<span *ngIf="isClaimAvailable(claim) else notAvailable" class="uk-label uk-label-success"
<div class="uk-margin-small-bottom">
<span *ngIf="isClaimAvailable(claim) else notAvailable" class="uk-label uk-label-small uk-label-success"
[attr.uk-tooltip]="'title: The link information is available in the portal and the APIs.'">available</span>
<ng-template #notAvailable>
<span class="uk-label uk-label-danger"
<span class="uk-label uk-label-small uk-label-danger"
[attr.uk-tooltip]="'title:The link information will be added in the portal and the APIs in the next content provision workflow.'">pending</span>
</ng-template>
</div>
<div class="uk-text-small">
<div *ngIf="showUserEmail" class="uk-margin-small-bottom">
<div *ngIf="showUserEmail" class="uk-margin-xsmall-bottom">
<span class="uk-text-meta">Claimed by:</span>
<span class="uk-margin-xsmall-left">{{claim.userMail}}</span>
</div>
@ -89,16 +108,15 @@
<icon class="uk-position-center" name="link" customClass="uk-text-primary" ratio="2" [flex]="true"></icon>
</div>
</div>
<div class="uk-width-1-2@m uk-width-1-1 uk-flex uk-flex-column uk-flex-center">
<div class="uk-width-1-2@m uk-width-1-1">
<claim-entity [entity]="claim.source" [type]="claim.sourceType" [source]="false" [properties]=properties
[externalPortalUrl]=externalPortalUrl></claim-entity>
</div>
</div>
</div>
<div class="uk-card-footer uk-flex uk-flex-right">
<div class="uk-width-xsmall uk-flex uk-flex-center uk-flex-middle">
<button class="uk-button uk-button-link uk-flex uk-flex-middle" (click)="deleteOpen(i)">
<icon name="delete" [flex]="true"></icon>
<span class="uk-margin-xsmall-left">Delete</span>
</button>
</div>
</li>
@ -107,6 +125,7 @@
<paging-no-load *ngIf="resultsNum" [currentPage]="page" [totalResults]="resultsNum" [size]="size"
(pageChange)="pageChange($event)"></paging-no-load>
</div>
</ng-container>
</div>
</div>
</div>

View File

@ -4,14 +4,4 @@
position: relative;
padding: 0 20px;
height: 100%;
&::before {
content: '';
position: absolute;
top: 0;
left: 50%;
right: 0;
bottom: 0;
border-left: @global-border-width solid @global-border;
}
}

View File

@ -52,6 +52,7 @@ export class DisplayClaimsComponent implements OnInit, OnDestroy {
lastIndexDate = null;
public filterForm: FormGroup;
public entities: string[] = [];
selected = [];
allOptions: Option[] = [
{label: OpenaireEntities.PUBLICATIONS, value: "publication"},
@ -310,23 +311,34 @@ export class DisplayClaimsComponent implements OnInit, OnDestroy {
}
isSelected(value: string) {
return this.filterForm && this.filterForm.get('entities').value.find(entity => entity === value)
return this.filterForm && this.filterForm.get('entities').value.find(entity => entity === value);
}
deleteOpen(index: number) {
deleteOpen(index: number = null) {
this.index = index;
this.deleteModal.alertTitle = 'Delete Confirmation';
this.deleteModal.message = 'Are you sure you want to delete this link?';
this.deleteModal.message = 'Are you sure you want to delete ' + (this.index != null ? '1' : this.selected.length) + ' link(s)?';
this.deleteModal.okButtonText = 'Yes';
this.deleteModal.open();
}
delete() {
this.subscriptions.push(this._claimService.deleteBulk([this.claims[this.index].id], this.properties.claimsAPIURL).subscribe(
let claimsToBeDeleted = ((this.index != null) ? [this.claims[this.index].id] : this.selected.map(claim => claim.id));
console.log(claimsToBeDeleted);
this.subscriptions.push(this._claimService.deleteBulk(claimsToBeDeleted, this.properties.claimsAPIURL).subscribe(
res => {
if (this.index != null) {
this.claims.splice(this.index, 1);
this.resultsNum = this.resultsNum - 1;
NotificationHandler.rise('Link has been deleted successfully');
} else {
claimsToBeDeleted.forEach(claimId => {
this.claims.splice(this.claims.findIndex((id) => id == claimId), 1);
});
this.resultsNum = this.resultsNum - claimsToBeDeleted.length;
NotificationHandler.rise(claimsToBeDeleted.length + ' links have been deleted successfully');
}
this.selected = [];
let goToPage = this.page;
if (this.totalPages(this.resultsNum) < this.page && this.page > 0) {
goToPage = this.page - 1;
@ -334,7 +346,8 @@ export class DisplayClaimsComponent implements OnInit, OnDestroy {
this.goTo(goToPage);
}, err => {
this.handleErrors(err, "Error deleting claim with id: " + this.claims[this.index].id);
}));
}
));
}
pageChange($event) {
@ -363,6 +376,43 @@ export class DisplayClaimsComponent implements OnInit, OnDestroy {
return totalPages;
}
selectClaim(item: any, event) {
let value = event.currentTarget.checked;
if (value) {
this.selected.push(item);
} else {
for (var _i = 0; _i < this.selected.length; _i++) {
let claim = this.selected[_i];
if (claim['id'] == item.id) {
this.selected.splice(_i, 1);
}
}
}
}
selectAll(event) {
let value = event.currentTarget.checked;
if (value) {
this.selected = [];
for (let _i = 0; _i < this.claims.length; _i++) {
let claim = this.claims[_i];
this.selected.push(claim);
}
} else {
this.selected = [];
}
}
isSelectedClaim(id: string) {
for (let _i = 0; _i < this.selected.length; _i++) {
let claim = this.selected[_i];
if (claim['id'] == id) {
return true;
}
}
return false;
}
private updateDescription(description: string) {
this._meta.updateTag({content: description}, "name='description'");
this._meta.updateTag({content: description}, "property='og:description'");

View File

@ -15,21 +15,22 @@ import {StringUtils} from "../../../utils/string-utils.class";
<div *ngIf="source" class="uk-text-small">
{{getEntityName(type)}}
</div>
<div class="uk-flex">
<span *ngIf="!source" class="uk-text-meta uk-margin-small-right uk-text-large uk-text-nowrap">Link to:</span>
<div>
<span *ngIf="!source" class="uk-text-meta uk-text-small uk-margin-small-right uk-text-nowrap">Link to:</span>
<publication-title [entity]="entity" param="id"
path="/search/result" [externalPortalUrl]=externalPortalUrl [linkAvailable]="linkAvailable"></publication-title>
</div>
</div>
<div *ngIf="type == 'project'" [attr.uk-tooptip]="getEntityName(type)"
class="uk-flex">
<span class="uk-text-meta uk-margin-small-right uk-text-large uk-text-nowrap">Link to:</span>
<div *ngIf="type == 'project'" [attr.uk-tooptip]="getEntityName(type)">
<span class="uk-text-meta uk-text-small uk-margin-small-right uk-text-nowrap">Link to:</span>
<project-title [project]="entity" [searchLink]=properties.searchLinkToProject
[externalPortalUrl]=externalPortalUrl></project-title>
</div>
<div *ngIf="type == 'context'" class="uk-flex uk-text-large">
<span class="uk-text-meta uk-margin-small-right uk-text-nowrap">Link to:</span>
<div *ngIf="type == 'context'">
<span class="uk-text-meta uk-text-small uk-margin-small-right uk-text-nowrap">Link to:</span>
<h6 class="uk-h6 uk-margin-remove">
<span class="uk-text-truncate" uk-tooltip="Concept">{{entity.title}}</span>
</h6>
</div>
`
})

View File

@ -20,7 +20,7 @@ import {properties} from "../../../../../environments/environment";
</h6>
</ng-container>
<span *ngIf="project['funderName']" class="uk-margin-small-top">
<span class="uk-text-muted">Funder: </span>{{project['funderName']}}
<span class="uk-text-meta">Funder: </span>{{project['funderName']}}
</span>
`
})

View File

@ -88,6 +88,10 @@ export class LayoutService {
* Handle it manually in the component, it doesn't use data
* */
private rootClassSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);
/**
* Display help pop-up on non-admin pages. (default true for the rest of the pages)
* */
private hasHelpPopUpSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
private subscriptions: any[] = [];
ngOnDestroy() {
@ -343,4 +347,12 @@ export class LayoutService {
this.rootClassSubject.next(value);
}
}
get hasHelpPopUp(): Observable<boolean> {
return this.hasHelpPopUpSubject.asObservable();
}
setHasHelpPopUp(value: boolean) {
this.hasHelpPopUpSubject.next(value);
}
}

View File

@ -26,7 +26,6 @@ import {ResultPreviewModule} from "../../utils/result-preview/result-preview.mod
import {FeedbackModule} from "../feedback/feedback.module";
import {TabsModule} from "../../utils/tabs/tabs.module";
import {LoadingModule} from "../../utils/loading/loading.module";
import {OrcidModule} from "../../orcid/orcid.module";
import {IconsModule} from "../../utils/icons/icons.module";
import {IconsService} from "../../utils/icons/icons.service";
import {cite, fire, graph, landmark, link, link_to, quotes, rocket, versions} from "../../utils/icons/icons";
@ -38,6 +37,7 @@ import {FullScreenModalModule} from "../../utils/modal/full-screen-modal/full-sc
import {SafeHtmlPipeModule} from '../../utils/pipes/safeHTMLPipe.module';
import {EntityActionsModule} from "../../utils/entity-actions/entity-actions.module";
import {ResultLandingRoutingModule} from "./resultLanding-routing.module";
import {OrcidCoreModule} from "../../orcid/orcid-core.module";
@NgModule({
imports: [
@ -47,7 +47,7 @@ import {ResultLandingRoutingModule} from "./resultLanding-routing.module";
AltMetricsModule, Schema2jsonldModule, SEOServiceModule,
DeletedByInferenceModule, ShowAuthorsModule, HelperModule, ResultLandingUtilsModule, AlertModalModule,
LandingHeaderModule, NoLoadPaging, ResultPreviewModule, FeedbackModule, TabsModule, LoadingModule,
OrcidModule, IconsModule, InputModule, EGIDataTransferModule, RecaptchaModule,
OrcidCoreModule, IconsModule, InputModule, EGIDataTransferModule, RecaptchaModule,
SdgFosSuggestModule, FullScreenModalModule, SafeHtmlPipeModule, EntityActionsModule
],
declarations: [

View File

@ -234,6 +234,7 @@ export class Role {
}
public static mapType(type: string, communityMap: boolean = true): string {
type = type.replace(this.GROUP, '');
if (type == "ri" && communityMap) {
type = "community";
} else if (type == "organization") {

View File

@ -14,16 +14,6 @@
(click)="deleteSectionOpen(number, i, 'number', 'delete')">
<icon name="close" [flex]="true"></icon>
</a>
<!-- <ng-container *ngIf="!stakeholder.defaultId">-->
<!-- <button [disabled]="editing || number.defaultId " class="md-btn md-btn-mini"-->
<!-- [title]="(number.defaultId?'Default sections cannot be deleted':'Delete all related sections')"-->
<!-- (click)="deleteSectionOpen(number, i, 'number', 'delete')"><i class="material-icons">highlight_off</i>-->
<!-- </button>-->
<!-- <button [disabled]="editing || number.defaultId " class="md-btn md-btn-mini"-->
<!-- [title]="(number.defaultId?'Default sections cannot be deleted':'Delete section and disconnect related')"-->
<!-- (click)="deleteSectionOpen(number, i, 'number', 'disconnect')"><i class="material-icons">link_off</i>-->
<!-- </button>-->
<!-- </ng-container>-->
</div>
</div>
<div *ngIf="numberSections.at(i)" class="uk-margin-medium-bottom">
@ -113,16 +103,6 @@
(click)="deleteSectionOpen(chart, i, 'chart', 'delete')">
<icon name="close" [flex]="true"></icon>
</a>
<!-- <ng-container *ngIf="!stakeholder.defaultId">-->
<!-- <button [disabled]="editing || chart.defaultId " class="md-btn md-btn-mini"-->
<!-- [title]="(chart.defaultId?'Default sections cannot be deleted':'Delete all related sections')"-->
<!-- (click)="deleteSectionOpen(chart, i, 'chart', 'delete')"><i class="material-icons">highlight_off</i>-->
<!-- </button>-->
<!-- <button [disabled]="editing || chart.defaultId " class="md-btn md-btn-mini"-->
<!-- [title]="(chart.defaultId?'Default sections cannot be deleted':'Delete section and disconnect related')"-->
<!-- (click)="deleteSectionOpen(chart, i, 'chart', 'disconnect')"><i class="material-icons">link_off</i>-->
<!-- </button>-->
<!-- </ng-container>-->
</div>
</div>
<div *ngIf="chartSections.at(i)"
@ -187,7 +167,6 @@
<img class="uk-width-1-1 uk-blend-multiply" [ngClass]="'uk-height-' + (indicator.height?indicator.height.toLowerCase():'medium')"
[src]="indicator.indicatorPaths[0].url">
</div>
<!--<ng-container *ngTemplateOutlet="description; context: {indicator:indicator}"></ng-container>-->
</div>
</div>
</div>
@ -219,6 +198,9 @@
</div>
</div>
</div>
<div *ngIf="loading" class="uk-position-relative uk-height-large">
<loading class="uk-position-center"></loading>
</div>
<modal-alert #editNumberModal
[large]="true" classTitle="uk-background-primary uk-light"
(alertOutput)="saveIndicator()"
@ -440,8 +422,6 @@
<iframe *ngIf="indicator.indicatorPaths[i].source !== 'image'" [class.uk-blend-multiply]="!isFullscreen"
[src]="indicator.indicatorPaths[i].safeResourceUrl" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"
class="uk-width-1-1 uk-height-large"></iframe>
<!-- <div *ngIf="properties.disableFrameLoad && indicator.indicatorPaths[i].source !== 'image'" class="uk-alert uk-alert-danger uk-text-center">I frames-->
<!-- preview is disabled</div>-->
<div *ngIf="indicator.indicatorPaths[i].source === 'image'">
<img class="uk-width-1-1 uk-height-large uk-blend-multiply" [src]="indicator.indicatorPaths[i].url">
</div>
@ -462,25 +442,10 @@
<div *ngIf="indicatorChildrenActionOnDelete == 'delete'" class="uk-text-bold">
Indicators of all profiles based on this default indicator, will be deleted as well.
</div>
<!-- <span *ngIf="indicatorChildrenActionOnDelete == 'disconnect'" class="uk-text-bold">-->
<!-- Indicators of all profiles based on this default indicator, will not be marked as copied from default anymore.-->
<!-- </span>-->
Are you sure you want to proceed?
<div #deleteNotify notify-form class="uk-width-1-1 uk-margin-medium-top"></div>
</div>
</modal-alert>
<!--<modal-alert #deleteAllModal (alertOutput)="deleteIndicator('delete')">
You are about to delete <span class="uk-text-bold" *ngIf="indicator && index !== -1">
"{{indicator.name ? indicator.name : indicator.indicatorPaths[0].parameters.title}}"</span> indicator permanently.
<span class="uk-text-bold">Indicators of all profiles based on this default indicator, will be deleted as well.</span>
Are you sure you want to proceed?
</modal-alert>
<modal-alert #deleteAndDisconnectModal (alertOutput)="deleteIndicator('disconnect')">
You are about to delete <span class="uk-text-bold" *ngIf="indicator && index !== -1">
"{{indicator.name ? indicator.name : indicator.indicatorPaths[0].parameters.title}}"</span> indicator permanently.
<span class="uk-text-bold">Indicators of all profiles based on this default indicator, will not be marked as copied from default anymore.</span>
Are you sure you want to proceed?
</modal-alert>-->
<modal-alert #deleteSectionModal (alertOutput)="deleteSection()" [overflowBody]="false" classTitle="uk-background-primary uk-light">
<div [class.uk-invisible]="editing" class="uk-position-relative">
<div *ngIf="editing">
@ -490,20 +455,9 @@
<div *ngIf="sectionChildrenActionOnDelete == 'delete' && !stakeholder.defaultId" class="uk-text-bold">
Sections of all profiles based on this default section and their contents, will be deleted as well.
</div>
<!-- <span *ngIf="sectionChildrenActionOnDelete == 'disconnect'" class="uk-text-bold">-->
<!-- Sections of all profiles based on this default section and their contents, will not be marked as copied from default anymore.-->
<!-- </span>-->
Are you sure you want to proceed?
</div>
</modal-alert>
<!--<modal-alert #deleteNumberSectionModal (alertOutput)="deleteSection('number')">
You are about to delete this section and its indicators permanently.
Are you sure you want to proceed?
</modal-alert>
<modal-alert #deleteChartSectionModal (alertOutput)="deleteSection()">
You are about to delete this section and its indicators permanently.
Are you sure you want to proceed?
</modal-alert>-->
<ng-template #new_section let-type="type">
<div class="section">
<div class="uk-flex uk-flex-center" (click)="createSection(-1, type)">

View File

@ -86,8 +86,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
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;
@ -354,7 +353,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
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].categories[this.categoryIndex].subCategories[this.subcategoryIndex] && !this.loading;
}
public get numberIndicatorPaths(): UntypedFormArray {
@ -504,16 +503,6 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
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."
} else {
this.urlParameterizedMessage = "This indicator couldn't be generated properly. Please make sure chart data is for the current stakeholder."
}
} else {
this.urlParameterizedMessage = null;
}
this.checkForSchemaEnhancements(this.numberIndicatorPaths.at(index).get('url').value);
if (this.indicator.indicatorPaths[index]) {
this.indicator.indicatorPaths[index] = indicatorPath;
@ -574,16 +563,6 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
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));
@ -601,12 +580,6 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
}
}
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));
}
private getJsonPathAsFormArray(indicatorPath: IndicatorPath): UntypedFormArray {
let jsonPath = this.fb.array([]);
if (indicatorPath.jsonPath) {
@ -900,19 +873,22 @@ 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();
}));
}
finish() {
this.editing = false;
this.loading = false;
}
reorderIndicators(sectionId: string, type: IndicatorType, reorder: Reorder) {
this.editing = true;
let path = [
@ -1159,24 +1135,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;
@ -1256,11 +1214,19 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
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) {
@ -1290,13 +1256,12 @@ 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);
indicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.indicatorUtils.getChartSource(chart.url), chart.url, chart.type, 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)) {
@ -1304,10 +1269,9 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
exists = true;
}
}
}
} else if (chart.type == "number") {
indicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(chart.url), chart.url, this.stakeholder,
indicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(chart.url), chart.url, 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) {
@ -1316,20 +1280,14 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
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]);
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 +1295,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 +1304,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
timeout: 6000,
pos: 'bottom-right'
});
this.editing = false;
this.importLoading = false;
this.finish();
}
}
@ -1408,8 +1353,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 +1367,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 +1386,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 +1396,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 +1428,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
timeout: 6000,
pos: 'bottom-right'
});
this.editing = false;
this.importLoading = false;
this.finish();
});
}

View File

@ -6,15 +6,15 @@ import {RouterModule} from '@angular/router';
import {ResultPreviewModule} from "../../utils/result-preview/result-preview.module";
import {ErrorMessagesModule} from "../../utils/errorMessages.module";
import {searcMyOrcidResultsComponent} from "./searchMyOrcidResults.component";
import {OrcidModule} from "../orcid.module";
import {NoLoadPaging} from "../../searchPages/searchUtils/no-load-paging.module";
import {PagingModule} from "../../utils/paging.module";
import {OrcidCoreModule} from "../orcid-core.module";
@NgModule({
imports: [
CommonModule, FormsModule,
RouterModule, ErrorMessagesModule,
ResultPreviewModule, OrcidModule, NoLoadPaging, PagingModule
ResultPreviewModule, OrcidCoreModule, NoLoadPaging, PagingModule
],
declarations: [
searcMyOrcidResultsComponent

View File

@ -0,0 +1,43 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule} from '@angular/router';
import {OrcidComponent} from './orcid.component';
import {OrcidService} from './orcid.service';
import {OrcidWorkComponent} from './orcid-work.component';
import {AlertModalModule} from '../utils/modal/alertModal.module';
import {ResultLandingService} from '../landingPages/result/resultLanding.service';
import {LoadingModule} from '../utils/loading/loading.module';
import {ResultLandingUtilsModule} from '../landingPages/landing-utils/resultLandingUtils.module';
import {IconsModule} from '../utils/icons/icons.module';
import {IconsService} from "../utils/icons/icons.service";
import {orcid_add, orcid_bin} from "../utils/icons/icons";
import {FullScreenModalModule} from "../utils/modal/full-screen-modal/full-screen-modal.module";
import {LogServiceModule} from "../utils/log/LogService.module";
import {OrcidRoutingModule} from "./orcid-routing.module";
@NgModule({
imports: [
CommonModule, RouterModule, AlertModalModule, LoadingModule, ResultLandingUtilsModule,
IconsModule, FullScreenModalModule, LogServiceModule
],
declarations: [
OrcidComponent,
OrcidWorkComponent
],
providers:[
OrcidService, ResultLandingService
],
exports: [
OrcidComponent,
OrcidWorkComponent
]
})
export class OrcidCoreModule{
constructor(private iconsService: IconsService) {
this.iconsService.registerIcons([orcid_add, orcid_bin])
}
}

View File

@ -1,43 +1,15 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule} from '@angular/router';
import {OrcidComponent} from './orcid.component';
import {OrcidService} from './orcid.service';
import {OrcidWorkComponent} from './orcid-work.component';
import {AlertModalModule} from '../utils/modal/alertModal.module';
import {ResultLandingService} from '../landingPages/result/resultLanding.service';
import {LoadingModule} from '../utils/loading/loading.module';
import {ResultLandingUtilsModule} from '../landingPages/landing-utils/resultLandingUtils.module';
import {IconsModule} from '../utils/icons/icons.module';
import {IconsService} from "../utils/icons/icons.service";
import {orcid_add, orcid_bin} from "../utils/icons/icons";
import {FullScreenModalModule} from "../utils/modal/full-screen-modal/full-screen-modal.module";
import {LogServiceModule} from "../utils/log/LogService.module";
import {OrcidRoutingModule} from "./orcid-routing.module";
import {OrcidCoreModule} from "./orcid-core.module";
import {OrcidComponent} from "./orcid.component";
@NgModule({
imports: [
CommonModule, RouterModule, OrcidRoutingModule, AlertModalModule, LoadingModule, ResultLandingUtilsModule,
IconsModule, FullScreenModalModule, LogServiceModule
],
declarations: [
OrcidComponent,
OrcidWorkComponent
],
providers:[
OrcidService, ResultLandingService
],
exports: [
OrcidComponent,
OrcidWorkComponent
]
imports: [OrcidCoreModule, OrcidRoutingModule],
exports: [OrcidComponent]
})
export class OrcidModule{
constructor(private iconsService: IconsService) {
this.iconsService.registerIcons([orcid_add, orcid_bin])
}
}

View File

@ -93,6 +93,8 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
public userInfoLinkPrefix = '';
@Input()
public userInfoLink = null;
@Input()
public relativeTo: ActivatedRoute = this._route;
public user: User;
public verification: any;
public code: UntypedFormControl;
@ -150,7 +152,7 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
'errorCode': LoginErrorCodes.NOT_LOGIN,
'redirectUrl': this._router.url
},
relativeTo: this._route
relativeTo: this.relativeTo
});
}
} else if(this.isMember) {

View File

@ -133,7 +133,14 @@ export class SearchResearchResultsService {
return this.http.get((properties.useLongCache && size == 0 && !params && (!refineQuery || !refineQuery.includes("fq="))) ? (properties.cacheUrl + encodeURIComponent(url)) : url)
.pipe(map(res => [res['meta'].total, this.parseResults(resultType, res['results'], properties), RefineResultsUtils.parse(res['refineResults'], refineFields, "publication")]));
}
countResults(field:string,value:string): any {
let url = properties.utilsService + "/portals/countResults";
if (field && value) {
url += "?field=" + encodeURIComponent(field) + "&value="+encodeURIComponent(value);
}
return this.http.get((properties.useLongCache ) ? (properties.cacheUrl + encodeURIComponent(url)) : url);
}
searchResultForEntity(resultType: string, params: string, page: number, size: number, properties: EnvProperties): any {
let link = properties.searchAPIURLLAst;
//let url = link+params+"/"+this.getEntityQueryName(resultType,true)+ "?format=json";

View File

@ -23,10 +23,11 @@ export class MenuItem {
isFeatured: boolean;
isActive: boolean;
target: string = "_blank";
badge?: string = ""; // used only for RDGraph portal (FAIRCORE4EOSC)
constructor(id: string, title: string, url: string, route: string, needsAuthorization: boolean, entitiesRequired: string[],
routeRequired: string[], params, icon: Icon = null, fragment = null, customClass = null, routeActive = null,
target: string = "_blank", type: string = "internal", isFeatured: boolean = false, items: MenuItem[] = []) {
target: string = "_blank", type: string = "internal", isFeatured: boolean = false, items: MenuItem[] = [], badge: string = "") {
this._id = id;
this.title = title;
this.url = url;
@ -43,6 +44,7 @@ export class MenuItem {
this.target = target;
this.type = type;
this.isFeatured = isFeatured;
this.badge = badge;
}
public static isTheActiveMenu(menu: MenuItem, currentRoute: any, activeMenuItem: string = ""): boolean {

View File

@ -41,7 +41,9 @@
<a *ngIf="!menu.url" [routerLink]="menu.route && (isEnabled([menu.route], showPage) || !menu.routeRequired) && menu.items.length === 0?menu.route:null"
(click)="menu.items.length === 0?closeCanvas(canvas):null"
[queryParams]="menu.params"
[fragment]="menu.fragment">{{menu.title}}<span *ngIf="menu.items.length > 0" class="uk-nav-parent-icon"></span></a>
[fragment]="menu.fragment">
<span *ngIf="menu.badge" style="position: relative"><span class="badge-mobile">{{menu.badge}}</span></span>
{{menu.title}}<span *ngIf="menu.items.length > 0" class="uk-nav-parent-icon"></span></a>
<a *ngIf="menu.url"
(click)="menu.items.length === 0?closeCanvas(canvas):null"
[href]="menu.items.length === 0?menu.url:null"
@ -215,7 +217,9 @@
<a *ngIf="menu.route.length == 0 && menu.url.length > 0"
href="{{menu.url}}" target="{{menu.target}}"
[class.custom-external]="menu.target != '_self'">{{menu.title}}</a>
<a *ngIf="(menu.route.length == 0 && menu.url.length == 0) || ( menu.route.length >0 && menu.routeRequired && !isEnabled([menu.route], showPage) && isAtleastOneEnabled(menu.routeRequired, showPage))">{{menu.title}}</a>
<a *ngIf="(menu.route.length == 0 && menu.url.length == 0) || ( menu.route.length >0 && menu.routeRequired && !isEnabled([menu.route], showPage) && isAtleastOneEnabled(menu.routeRequired, showPage))">
<span *ngIf="menu.badge" class="badge">{{menu.badge}}</span>
{{menu.title}}</a>
<div *ngIf="menu.items.length > 0" class="uk-navbar-dropdown uk-navbar-dropdown-bottom-left uk-height-max-medium uk-overflow-auto">
<!-- Do not delete this div, because it will remove the margin bottom of navbar -->
<div>

View File

@ -6,11 +6,14 @@ import {
ElementRef, EventEmitter,
Input, OnDestroy, Output,
QueryList,
ViewChild
ViewChild,
Inject,
PLATFORM_ID
} from "@angular/core";
import {SliderTabComponent} from "./slider-tab.component";
import {ActivatedRoute, Router} from "@angular/router";
import {Subscription} from "rxjs";
import {isPlatformServer} from "@angular/common";
import Timeout = NodeJS.Timeout;
declare var UIkit;
@ -20,7 +23,7 @@ declare var UIkit;
template: `
<div #sliderElement class="uk-position-relative" [class.uk-slider]="position === 'horizontal'"
[ngClass]="customClass">
<div [class.uk-slider-container-tabs]="position === 'horizontal'" [class.uk-border-bottom]="border && position === 'horizontal'">
<div [class.uk-slider-container-tabs]="position === 'horizontal'" [class.uk-border-bottom]="border && position === 'horizontal'" [ngClass]="containerClass">
<ul #tabsElement [class.uk-flex-nowrap]="position === 'horizontal'"
[class.uk-slider-items]="position === 'horizontal'"
[class.uk-tab-left]="position === 'left'" [class.uk-tab-right]="position === 'right'"
@ -78,8 +81,10 @@ declare var UIkit;
</ng-container>
</ul>
</div>
<ng-container *ngIf="!isServer">
<slider-arrow *ngIf="position === 'horizontal' && arrows" type="previous"></slider-arrow>
<slider-arrow *ngIf="position === 'horizontal' && arrows" type="next"></slider-arrow>
</ng-container>
</div>
`,
})
@ -112,6 +117,11 @@ export class SliderTabsComponent implements AfterViewInit, OnDestroy {
* */
@Input()
public flexPosition: 'center' | 'left' | 'right' = 'left';
/**
* Set a class for the container
* */
@Input()
public containerClass: string;
/**
* Set a class above tabs
* */
@ -138,10 +148,13 @@ export class SliderTabsComponent implements AfterViewInit, OnDestroy {
private subscriptions: any[] = [];
private observer: IntersectionObserver;
private timeout: Timeout;
isServer: boolean;
constructor(private route: ActivatedRoute,
private router: Router,
private cdr: ChangeDetectorRef) {
private cdr: ChangeDetectorRef,
@Inject(PLATFORM_ID) private platform: any) {
this.isServer = isPlatformServer(this.platform);
}
ngAfterViewInit() {
@ -164,7 +177,7 @@ export class SliderTabsComponent implements AfterViewInit, OnDestroy {
}
});
if (this.type === 'static') {
let tabs = UIkit.tab(this.tabsElement.nativeElement, {connect: this.connect});
let tabs = UIkit.switcher(this.tabsElement.nativeElement, {connect: this.connect});
tabs.show(this.activeIndex);
if (this.connect.includes('#')) {
this.scrollToStart();

View File

@ -333,6 +333,14 @@ export class SearchFieldsBase {
operator: "pf",
equalityOperator: " = ",
filterType: "triplet"
},
["haslicense"]: {
name: "License",
type: "triplet",
param: "haslicense",
operator: "hl",
equalityOperator: " = ",
filterType: "triplet"
}
};
@ -369,6 +377,11 @@ export class SearchFieldsBase {
{ name: "All", id: "", count: "0" },
{ name: "Yes", id: "true", count: "0" },
{ name: "No", id: "false", count: "0" }
],
["haslicense"]: [
{ name: "All", id: "", count: "0" },
{ name: "Yes", id: "true", count: "0" },
{ name: "No", id: "false", count: "0" }
]
};

View File

@ -4,15 +4,15 @@ import {ResultPreviewComponent} from "./result-preview.component";
import {RouterModule} from "@angular/router";
import {ShowAuthorsModule} from "../authors/showAuthors.module";
import {ResultLandingUtilsModule} from "../../landingPages/landing-utils/resultLandingUtils.module";
import {OrcidModule} from "../../orcid/orcid.module";
import {IconsModule} from "../icons/icons.module";
import {IconsService} from "../icons/icons.service";
import {cite, fire, landmark, link, link_to, quotes, rocket} from "../icons/icons";
import {EntityActionsModule} from "../entity-actions/entity-actions.module";
import {EntityMetadataModule} from "../../landingPages/landing-utils/entity-metadata.module";
import {OrcidCoreModule} from "../../orcid/orcid-core.module";
@NgModule({
imports: [CommonModule, RouterModule, ShowAuthorsModule, ResultLandingUtilsModule, OrcidModule, IconsModule, EntityActionsModule, EntityMetadataModule],
imports: [CommonModule, RouterModule, ShowAuthorsModule, ResultLandingUtilsModule, OrcidCoreModule, IconsModule, EntityActionsModule, EntityMetadataModule],
declarations: [ResultPreviewComponent],
exports: [ResultPreviewComponent]
})