diff --git a/explore/src/app/app-routing.module.ts b/explore/src/app/app-routing.module.ts
index dfd6c7fb..a24b2e53 100644
--- a/explore/src/app/app-routing.module.ts
+++ b/explore/src/app/app-routing.module.ts
@@ -10,6 +10,7 @@ const routes: Routes = [
{path: 'mail-preferences', loadChildren: () => import('./userEmailPreferences/mailPrefs.module').then(m => m.LibMailPrefsModule)},
{path: 'sdgs', loadChildren: () => import('./sdg/sdg.module').then(m => m.LibSdgModule)},
{path: 'fields-of-science', loadChildren: () => import('./fos/fos.module').then(m => m.LibFosModule), data: {extraOffset: 100}},
+ // {path: 'funders', loadChildren: () => import('./funders/funders.module').then(m => m.FundersModule)},
{path: 'contact-us', loadChildren: () => import('./contact/contact.module').then(m => m.ContactModule), data: {hasQuickContact: false}},
// ORCID Pages
{path: 'orcid', loadChildren: () => import('./orcid/orcid.module').then(m => m.LibOrcidModule)},
diff --git a/explore/src/app/app.component.ts b/explore/src/app/app.component.ts
index 97c796c7..5292ea5c 100644
--- a/explore/src/app/app.component.ts
+++ b/explore/src/app/app.component.ts
@@ -191,6 +191,7 @@ export class AppComponent {
new MenuItem("", "Registries", "", "/search/entity-registries", false, ["datasource"], ["/search/entity-registries"], {}),
new MenuItem("", "Browse all", "", "/search/find/dataproviders", false, ["datasource"], ["/search/find/dataproviders"], {})]
),
+ // new MenuItem("funders", "Funders", "", "/funders", false, [], ["/funders"], {}),
];
if (Session.isPortalAdministrator(this.user)) {
this.userMenuItems.push(new MenuItem("", "Manage all links", "", "/claims", false, [], ["/claims"], {}));
diff --git a/explore/src/app/funders/funders-routing.module.ts b/explore/src/app/funders/funders-routing.module.ts
new file mode 100644
index 00000000..5359a285
--- /dev/null
+++ b/explore/src/app/funders/funders-routing.module.ts
@@ -0,0 +1,19 @@
+import {NgModule} from '@angular/core';
+import {RouterModule} from '@angular/router';
+
+import {FundersComponent} from './funders.component';
+import {IsRouteEnabled} from "../openaireLibrary/error/isRouteEnabled.guard";
+import {PreviousRouteRecorder} from "../openaireLibrary/utils/piwik/previousRouteRecorder.guard";
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([
+ {
+ path: '', component: FundersComponent,
+ canActivate: [IsRouteEnabled],
+ canDeactivate: [PreviousRouteRecorder]
+ }
+ ])
+ ]
+})
+export class FundersRoutingModule { }
diff --git a/explore/src/app/funders/funders.component.html b/explore/src/app/funders/funders.component.html
new file mode 100644
index 00000000..2844f376
--- /dev/null
+++ b/explore/src/app/funders/funders.component.html
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
+ Funders
+
+
+
+ Be an integral part of the open R&I ecosystem
+
+
+ Lorem ipsum dolor sit amet, consetetur sadipscing
+ {{formatNumber(projectsNumber)}} grants from
+ {{formatNumber(fundersNumber)}} funders, linked to
+ {{formatNumber(researchProductsNumber)}} funded research outputs.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{(funder.openAccessResearchProducts / funder.researchProducts) * 100 | number : '1.0-0'}}%
+
+
Open Access
+
+
+
+
+
+
+
+ {{funder.alias}}
+
+
+
+
+
+ {{funder.name}}
+
+
+
+
+
+
+
+
pageSize"
+ (pageChange)="updateCurrentPage($event)"
+ [currentPage]="currentPage" [size]="pageSize"
+ [totalResults]="groupedFunders[index].data.length">
+
+
+
+
+
\ No newline at end of file
diff --git a/explore/src/app/funders/funders.component.less b/explore/src/app/funders/funders.component.less
new file mode 100644
index 00000000..32d0b5d5
--- /dev/null
+++ b/explore/src/app/funders/funders.component.less
@@ -0,0 +1,36 @@
+@import (reference) "~src/assets/openaire-theme/less/color.less";
+
+
+.custom-coins-dot:after {
+ content: "";
+ background-image: url("~src/assets/explore-assets/funders/coins.svg");
+ display: inline-block;
+ background-size: 100% 100%;
+ height: 35px;
+ width: 35px;
+ margin-right: 10px;
+}
+
+.custom-handshake-dot:after {
+ content: "";
+ background-image: url("~src/assets/explore-assets/funders/handshake.svg");
+ display: inline-block;
+ background-size: 100% 100%;
+ height: 30px;
+ width: 30px;
+ margin-left: 10px;
+}
+
+.setType(@color) {
+ border-bottom: 4px solid fade(@color, 30%);
+
+ & .type {
+ color: @color;
+ }
+}
+
+.uk-card {
+ &.funder {
+ .setType(@funder-color);
+ }
+}
\ No newline at end of file
diff --git a/explore/src/app/funders/funders.component.ts b/explore/src/app/funders/funders.component.ts
new file mode 100644
index 00000000..b3b44da3
--- /dev/null
+++ b/explore/src/app/funders/funders.component.ts
@@ -0,0 +1,260 @@
+import {Component, OnInit} from '@angular/core';
+import {Router} from '@angular/router';
+import {Subscriber, Subscription, zip} from "rxjs";
+import {Meta, Title} from "@angular/platform-browser";
+
+import {EnvProperties} from "../openaireLibrary/utils/properties/env-properties";
+import {PiwikService} from "../openaireLibrary/utils/piwik/piwik.service";
+import {SEOService} from "../openaireLibrary/sharedComponents/SEO/SEO.service";
+import {Breadcrumb} from "../openaireLibrary/utils/breadcrumbs/breadcrumbs.component";
+import {properties} from "../../environments/environment";
+import {RefineFieldResultsService} from '../openaireLibrary/services/refineFieldResults.service';
+import {StakeholderService} from '../openaireLibrary/monitor/services/stakeholder.service';
+import {Option} from '../openaireLibrary/sharedComponents/input/input.component';
+import {StringUtils} from '../openaireLibrary/utils/string-utils.class';
+import {HelperFunctions} from '../openaireLibrary/utils/HelperFunctions.class';
+import {NumberUtils} from '../openaireLibrary/utils/number-utils.class';
+
+@Component({
+ selector: 'funders',
+ templateUrl: './funders.component.html',
+ styleUrls: ['funders.component.less']
+})
+
+export class FundersComponent implements OnInit {
+ private subscriptions: Subscription[] = [];
+ url: string = null;
+ pageTitle: string = "OpenAIRE - Explore | Funders";
+ pageDescription: string = "Funders | Be an integral part of the open R&I ecosystem";
+ properties: EnvProperties = properties;
+ breadcrumbs: Breadcrumb[] = [{name: 'home', route: '/'}, {name: 'funders'}];
+ showLoading: boolean = true;
+ index: number = 0;
+ funders: any[] = [];
+ groupedFunders: any[] = [];
+ options: Option[];
+ sortBy: string = 'all';
+ currentPage: number = 1;
+ pageSize: number = 6;
+ fundersNumber: number = 0;
+ researchProductsNumber: number = 0;
+ projectsNumber: number = 0;
+ fundersMap = new Map();
+
+ constructor(private router: Router,
+ private meta: Meta,
+ private title: Title,
+ private seoService: SEOService,
+ private piwikService: PiwikService,
+ private refineFieldResultsService: RefineFieldResultsService,
+ private stakeholderService: StakeholderService) {
+ }
+
+ ngOnInit() {
+ this.title.setTitle('OpenAIRE - Explore | Funders');
+ this.properties = properties;
+ if (this.properties.enablePiwikTrack && (typeof document !== 'undefined')) {
+ this.subscriptions.push( this.piwikService.trackView(this.properties, this.pageTitle, this.properties.piwikSiteId).subscribe());
+ }
+ this.url = this.properties.domain + this.router.url;
+ this.seoService.createLinkForCanonicalURL(this.url);
+ this.updateUrl(this.url);
+ this.updateTitle(this.pageTitle);
+ this.updateDescription(this.pageDescription);
+ this.options = [
+ {value: 'all', label: 'All funders'},
+ {value: 'dashboard', label: 'Funders with dashboard'}
+ ];
+ this.getFunders();
+ }
+
+ ngOnDestroy() {
+ this.subscriptions.forEach(subscription => {
+ if (subscription instanceof Subscriber) {
+ subscription.unsubscribe();
+ }
+ });
+ }
+
+ private updateDescription(description: string) {
+ this.meta.updateTag({content: description}, "name='description'");
+ this.meta.updateTag({content: description}, "property='og:description'");
+ }
+
+ private updateTitle(title: string) {
+ var title = ((title.length > 50) ? title.substring(0, 50) : title);
+ this.title.setTitle(title);
+ this.meta.updateTag({content: title}, "property='og:title'");
+ }
+
+ private updateUrl(url: string) {
+ this.meta.updateTag({content: url}, "property='og:url'");
+ }
+
+ private getFunders() {
+ let refineParams = '&fq=resultbestaccessright%20exact%20%22Open%20Access%22';
+ this.subscriptions.push(
+ zip(
+ this.refineFieldResultsService.getRefineFieldsResultsByEntityName(['relfunder'], 'result', this.properties),
+ this.refineFieldResultsService.getRefineFieldsResultsByEntityName(['relfunder'], 'result', this.properties, refineParams),
+ this.refineFieldResultsService.getRefineFieldsResultsByEntityName(['funder'], 'project', this.properties),
+ this.stakeholderService.getStakeholders(this.properties.monitorServiceAPIURL, 'funder')
+ ).subscribe((data: any[]) => {
+ // storing all needed data to a map
+ // 1st call
+ let queriedFunders1 = data[0][1][0].values;
+ queriedFunders1.forEach(queriedFunder => {
+ this.fundersMap.set(queriedFunder.id, {
+ "id": queriedFunder.id,
+ "name": queriedFunder.name,
+ "alias": '',
+ "researchProducts": +queriedFunder.number,
+ "openAccessResearchProducts": 0,
+ "projects": 0,
+ "monitorDashboard": '',
+ "monitorDashboardStatus": '',
+ "logoUrl": ''
+ });
+ });
+
+ // 2nd call
+ let queriedFunders2 = data[1][1][0].values;
+ queriedFunders2.forEach(queriedFunder => {
+ if(this.fundersMap.has(queriedFunder.id)) {
+ this.fundersMap.get(queriedFunder.id).openAccessResearchProducts = +queriedFunder.number;
+ }
+ });
+
+ // 3rd call
+ let queriedFunders3 = data[2][1][0].values;
+ queriedFunders3.forEach(queriedFunder => {
+ if(this.fundersMap.has(queriedFunder.id)) {
+ this.fundersMap.get(queriedFunder.id).projects = +queriedFunder.number;
+ } else {
+ this.fundersMap.set(queriedFunder.id, {
+ "id": queriedFunder.id,
+ "name": queriedFunder.name,
+ "alias": '',
+ "researchProducts": 0,
+ "openAccessResearchProducts": 0,
+ "projects": +queriedFunder.number,
+ "monitorDashboard": '',
+ "monitorDashboardStatus": '',
+ "logoUrl": ''
+ });
+ }
+ });
+
+ // 4th call
+ let queriedFunders4 = data[3];
+ queriedFunders4.forEach(queriedFunder => {
+ let id = queriedFunder.index_id + '||' + queriedFunder.index_name + '||' + queriedFunder.index_shortName;
+ if(this.fundersMap.has(id)) {
+ this.fundersMap.get(id).alias = queriedFunder.alias;
+ this.fundersMap.get(id).monitorDashboard = queriedFunder.alias;
+ this.fundersMap.get(id).monitorDashboardStatus = queriedFunder.visibility;
+ this.fundersMap.get(id).logoUrl = queriedFunder.logoUrl;
+ } else {
+ this.fundersMap.set(id, {
+ "id": queriedFunder.id,
+ "name": queriedFunder.name,
+ "alias": queriedFunder.alias,
+ "researchProducts": 0,
+ "openAccessResearchProducts": 0,
+ "projects": 0,
+ "monitorDashboard": queriedFunder.alias,
+ "monitorDashboardStatus": queriedFunder.visibility,
+ "logoUrl": queriedFunder.logoUrl
+ });
+ }
+ });
+ // convert funders map into an array
+ this.funders = Array.from(this.fundersMap.values());
+ // group funders based on their initial letter
+ this.groupFunders(this.funders);
+ // calculate total numbers for intro content
+ this.calculateNumbers();
+
+ this.showLoading = false;
+ })
+ );
+ }
+
+ private groupFunders(funders) {
+ if(funders.length === 0) {
+ return [];
+ }
+ funders.sort((a, b) => a['name'].localeCompare(b['name']));
+ this.index = 0;
+ this.groupedFunders = Object.values(
+ funders.reduce((acc, funder) => {
+ let firstLetter = funder['name'][0].toLocaleUpperCase();
+ if(!acc[firstLetter]) {
+ acc[firstLetter] = {group: firstLetter, data: [funder]};
+ } else {
+ acc[firstLetter].data.push(funder);
+ }
+ return acc;
+ },{})
+ )
+ if(funders.length > 1) {
+ this.groupedFunders.unshift({group: 'All', data: funders});
+ }
+ }
+
+ private calculateSum(array, property) {
+ let sum = 0;
+ array.forEach(element => {
+ sum += element[property];
+ });
+ return sum;
+ }
+
+ private calculateNumbers() {
+ this.fundersNumber = this.funders.length;
+ this.researchProductsNumber = this.calculateSum(this.funders, 'researchProducts');
+ this.projectsNumber = this.calculateSum(this.funders, 'projects');
+ }
+
+ get showContentWithNumbers() {
+ return this.fundersNumber && this.researchProductsNumber && this.projectsNumber;
+ }
+
+ formatNumber(num: number | string) {
+ let formatted = NumberUtils.roundNumber(+num);
+ return formatted.number + formatted.size;
+ }
+
+ changeDisplayedFunders(i) {
+ this.currentPage = 1;
+ this.index = i;
+ }
+
+ urlEncodeAndQuote(str: string): string {
+ return StringUtils.quote(StringUtils.URIEncode(str));
+ }
+
+ sortByChanged() {
+ let displayedFunders = this.funders;
+ if(this.sortBy == 'dashboard') {
+ displayedFunders = this.funders.filter(funder => funder.monitorDashboard && funder.monitorDashboard?.length > 0 && funder.monitorDashboardStatus != 'PRIVATE');
+ }
+ this.currentPage = 1;
+ this.groupFunders(displayedFunders);
+ }
+
+ public updateCurrentPage($event) {
+ this.currentPage = $event.value;
+ HelperFunctions.scrollToId('target');
+ }
+}
diff --git a/explore/src/app/funders/funders.module.ts b/explore/src/app/funders/funders.module.ts
new file mode 100644
index 00000000..dbaa3bbf
--- /dev/null
+++ b/explore/src/app/funders/funders.module.ts
@@ -0,0 +1,46 @@
+import {NgModule} from '@angular/core';
+import {CommonModule} from '@angular/common';
+import {RouterModule} from '@angular/router';
+
+import {FundersComponent} from './funders.component';
+import {FundersRoutingModule} from "./funders-routing.module";
+import {AlertModalModule} from "../openaireLibrary/utils/modal/alertModal.module";
+import {PiwikService} from "../openaireLibrary/utils/piwik/piwik.service";
+import {IsRouteEnabled} from "../openaireLibrary/error/isRouteEnabled.guard";
+import {Schema2jsonldModule} from "../openaireLibrary/sharedComponents/schema2jsonld/schema2jsonld.module";
+import {SEOServiceModule} from "../openaireLibrary/sharedComponents/SEO/SEOService.module";
+import {BreadcrumbsModule} from "../openaireLibrary/utils/breadcrumbs/breadcrumbs.module";
+import {LoadingModule} from "../openaireLibrary/utils/loading/loading.module";
+import {RefineFieldResultsServiceModule} from '../openaireLibrary/services/refineFieldResultsService.module';
+import {LogoUrlPipeModule} from '../openaireLibrary/utils/pipes/logoUrlPipe.module';
+import {IconsModule} from '../openaireLibrary/utils/icons/icons.module';
+import {IconsService} from '../openaireLibrary/utils/icons/icons.service';
+import {open_access} from '../openaireLibrary/utils/icons/icons';
+import {closed_access} from '../openaireLibrary/utils/icons/icons';
+import {InputModule} from '../openaireLibrary/sharedComponents/input/input.module';
+import {PagingModule} from '../openaireLibrary/utils/paging.module';
+
+
+@NgModule({
+ imports: [
+ FundersRoutingModule, CommonModule, RouterModule, AlertModalModule,
+ Schema2jsonldModule, SEOServiceModule, BreadcrumbsModule, LoadingModule,
+ RefineFieldResultsServiceModule, LogoUrlPipeModule, IconsModule, InputModule,
+ PagingModule
+ ],
+ declarations: [
+ FundersComponent
+ ],
+ providers: [
+ PiwikService, IsRouteEnabled
+ ],
+ exports: [
+ FundersComponent
+ ]
+})
+
+export class FundersModule {
+ constructor(private iconsService: IconsService) {
+ this.iconsService.registerIcons([open_access, closed_access]);
+ }
+}
diff --git a/explore/src/app/openaireLibrary b/explore/src/app/openaireLibrary
index 1d31c7b2..7e5317e9 160000
--- a/explore/src/app/openaireLibrary
+++ b/explore/src/app/openaireLibrary
@@ -1 +1 @@
-Subproject commit 1d31c7b23444ab7377477b54ff11cc3662ccf0c2
+Subproject commit 7e5317e920340db31fff4eca37497b5ab37e50c7
diff --git a/explore/src/assets/common-assets b/explore/src/assets/common-assets
index 8e96d241..0b4b42ee 160000
--- a/explore/src/assets/common-assets
+++ b/explore/src/assets/common-assets
@@ -1 +1 @@
-Subproject commit 8e96d2414f01589fa7abe8a2f9f33dc94fc3e2ca
+Subproject commit 0b4b42ee9584da0237f307ebd22f68fbdeb978a4
diff --git a/explore/src/assets/explore-assets/funders/coins.svg b/explore/src/assets/explore-assets/funders/coins.svg
new file mode 100644
index 00000000..6b3d73fb
--- /dev/null
+++ b/explore/src/assets/explore-assets/funders/coins.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/explore/src/assets/explore-assets/funders/funders.png b/explore/src/assets/explore-assets/funders/funders.png
new file mode 100644
index 00000000..0ff764f5
Binary files /dev/null and b/explore/src/assets/explore-assets/funders/funders.png differ
diff --git a/explore/src/assets/explore-assets/funders/handshake.svg b/explore/src/assets/explore-assets/funders/handshake.svg
new file mode 100644
index 00000000..03470325
--- /dev/null
+++ b/explore/src/assets/explore-assets/funders/handshake.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/explore/src/assets/explore-custom.less b/explore/src/assets/explore-custom.less
index f53269c3..3109db9f 100644
--- a/explore/src/assets/explore-custom.less
+++ b/explore/src/assets/explore-custom.less
@@ -33,6 +33,14 @@
/* Landing */
@landing-portal-color: @explore-color;
-.deposit {
+.deposit, .funders {
@import (multiple) "~src/assets/openaire-theme/less/_import";
}
+
+.monitor-dashboard-link {
+ color: @monitor-color;
+}
+
+.funders-title {
+ color: @primary-color;
+}
diff --git a/explore/src/assets/openaire-theme b/explore/src/assets/openaire-theme
index a49dca9a..4794bc9f 160000
--- a/explore/src/assets/openaire-theme
+++ b/explore/src/assets/openaire-theme
@@ -1 +1 @@
-Subproject commit a49dca9a915f04fb85f86eb446d045056bbe229c
+Subproject commit 4794bc9f623dcf90e85f45f569269b9c25911f44
diff --git a/explore/src/environments/environment.beta.ts b/explore/src/environments/environment.beta.ts
index 90db5608..9f183019 100644
--- a/explore/src/environments/environment.beta.ts
+++ b/explore/src/environments/environment.beta.ts
@@ -61,6 +61,7 @@ export let properties: EnvProperties = {
datasourcesAPI: "https://beta.services.openaire.eu/openaire/ds/api/",
+ monitorServiceAPIURL: "https://beta.services.openaire.eu/uoa-monitor-service",
adminToolsAPIURL: "https://beta.services.openaire.eu/uoa-admin-tools/",
adminToolsCommunity: "openaire",
diff --git a/explore/src/environments/environment.prod.ts b/explore/src/environments/environment.prod.ts
index 46e0355c..084c6848 100644
--- a/explore/src/environments/environment.prod.ts
+++ b/explore/src/environments/environment.prod.ts
@@ -65,6 +65,7 @@ export let properties: EnvProperties = {
datasourcesAPI: "https://services.openaire.eu/openaire/ds/api/",
+ monitorServiceAPIURL: "https://services.openaire.eu/uoa-monitor-service",
adminToolsAPIURL: "https://services.openaire.eu/uoa-admin-tools/",
adminToolsCommunity: "openaire",
diff --git a/explore/src/environments/environment.ts b/explore/src/environments/environment.ts
index bfee0b7d..4ebf0dca 100644
--- a/explore/src/environments/environment.ts
+++ b/explore/src/environments/environment.ts
@@ -66,6 +66,7 @@ export let properties: EnvProperties = {
cacheUrl: "http://dl170.madgik.di.uoa.gr:3000/get?url=",
+ monitorServiceAPIURL: "http://duffy.di.uoa.gr:19380/uoa-monitor-service",
adminToolsAPIURL: "http://duffy.di.uoa.gr:19280/uoa-admin-tools/",
adminToolsCommunity: "openaire",