diff --git a/dashboard/users/role-users/role-users.component.ts b/dashboard/users/role-users/role-users.component.ts index 6ce7517b..bf02493e 100644 --- a/dashboard/users/role-users/role-users.component.ts +++ b/dashboard/users/role-users/role-users.component.ts @@ -256,9 +256,14 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges { NotificationHandler.rise('Group has been successfully created'); this.updateLists(); }, error => { - NotificationHandler.rise('An error has occurred. Please try again later', 'danger'); - this.loadActive = false; - this.loadPending = false; + if(error.status === 409) { + NotificationHandler.rise('Group already exists. You can try to invite a ' + this.role + ' instead.', 'warning'); + this.updateLists(); + } else { + NotificationHandler.rise('An error has occurred. Please try again later', 'danger'); + this.loadActive = false; + this.loadPending = false; + } }); } diff --git a/login/adminLoginGuard.guard.ts b/login/adminLoginGuard.guard.ts index 6c01ac49..5c5fb990 100644 --- a/login/adminLoginGuard.guard.ts +++ b/login/adminLoginGuard.guard.ts @@ -28,7 +28,9 @@ export class AdminLoginGuard implements CanActivate, CanActivateChild { } return Session.isPortalAdministrator(user) || (data.monitorCurator && Session.isMonitorCurator(user)) || - (data.communityCurator && Session.isCommunityCurator(user)) + (data.communityCurator && Session.isCommunityCurator(user)) || + (data.monitorManager && Session.isKindOfMonitorManager(user)) || + (data.communityManager && Session.isKindOfCommunityManager(user)) }),tap(isAdmin => { if(!isAdmin) { this.router.navigate(['/user-info'], { diff --git a/monitor/indicators/indicators.component.ts b/monitor/indicators/indicators.component.ts index b5cd56a2..eb9e0fd5 100644 --- a/monitor/indicators/indicators.component.ts +++ b/monitor/indicators/indicators.component.ts @@ -3,8 +3,9 @@ import {Subscriber} from "rxjs"; import {properties} from "../../../../environments/environment"; import {ActivatedRoute, Router} from "@angular/router"; import {HelperService} from "../../utils/helper/helper.service"; -import {StakeholderService} from "../services/stakeholder.service"; -import {ConfigurationService} from "../../utils/configuration/configuration.service"; +import {Meta, Title} from "@angular/platform-browser"; +import {SEOService} from "../../sharedComponents/SEO/SEO.service"; +import {ResourcesService} from "../services/resources.service"; @Component({ selector: 'indicators-page', @@ -12,28 +13,43 @@ import {ConfigurationService} from "../../utils/configuration/configuration.serv }) export class IndicatorsComponent implements OnInit, OnDestroy { private subscriptions: any[] = []; + public types = ResourcesService.types; public properties = properties; public pageContents; constructor(private helper: HelperService, - private configurationService: ConfigurationService, + private resourcesService: ResourcesService, private router: Router, - private stakeholderService: StakeholderService, + private meta: Meta, + private title: Title, + private seoService: SEOService, private route: ActivatedRoute) { } ngOnInit() { this.subscriptions.push(this.route.params.subscribe(params => { - if(params['stakeholder']) { - this.subscriptions.push(this.stakeholderService.getStakeholderAsObservable().subscribe(stakeholder => { - this.getPageContents(stakeholder.type); - })); - } else if(params['type']){ - this.getPageContents(params['type']); + if(params['type']){ + let type = this.types.find(type => type.value === params['type']); + if(type) { + this.getPageContents(type.value); + const description = "Monitor | Indicators " + type.label; + const title = "Monitor | Indicators " + type.label; + this.metaTags(title, description); + } } })) } + metaTags(title, description) { + const url = properties.domain + properties.baseLink + this.router.url; + this.seoService.createLinkForCanonicalURL(url, false); + this.meta.updateTag({content: url}, "property='og:url'"); + this.meta.updateTag({content: description}, "name='description'"); + this.meta.updateTag({content: description}, "property='og:description'"); + this.meta.updateTag({content: title}, "property='og:title'"); + this.title.setTitle(title); + } + ngOnDestroy() { this.subscriptions.forEach(subscription => { if (subscription instanceof Subscriber) { @@ -43,8 +59,8 @@ export class IndicatorsComponent implements OnInit, OnDestroy { } public getPageContents(type: string) { - this.subscriptions.push(this.configurationService.isPageEnabled('monitor', '/indicators/' + type, 'monitor').subscribe(isEnabled => { - if(isEnabled) { + this.subscriptions.push(this.resourcesService.isPagesEnabled().subscribe(status => { + if(status[0]) { this.subscriptions.push(this.helper.getPageHelpContents(this.properties, 'monitor', '/indicators/' + type, 'monitor').subscribe(contents => { this.pageContents = contents; })); diff --git a/monitor/methodology/methodology.component.ts b/monitor/methodology/methodology.component.ts new file mode 100644 index 00000000..5a84b1a5 --- /dev/null +++ b/monitor/methodology/methodology.component.ts @@ -0,0 +1,465 @@ +import {Component, OnDestroy, OnInit} from "@angular/core"; +import {Subscription} from "rxjs"; +import {Meta, Title} from "@angular/platform-browser"; +import {ActivatedRoute, Router} from "@angular/router"; +import {Stakeholder} from "../entities/stakeholder"; +import {OpenaireEntities} from "../../utils/properties/searchFields"; +import {StakeholderService} from "../services/stakeholder.service"; +import {SEOService} from "../../sharedComponents/SEO/SEO.service"; +import {properties} from "../../../../environments/environment"; + +@Component({ + selector: 'methodology', + template: ` +
+
+

Terminology and
construction.

+
+
+ + +
+ + More information for + OpenAIRE Research Graph + . +
+
+
+

+ Inclusion, transparency,
quality, state of the art
technology. +

+
+

Our methodological approach is based on the following operational quality criteria:

+
    +
  • Openness and transparency: Methodological assumptions are openly and clearly presented.
  • +
  • Coverage and accuracy: As detailed in graph.openaire.eu + multiple data sources are ingested in the OpenAIRE research graph for coverage to the fullest extent possible, in order to provide meaningful indicators.
  • +
  • Clarity and replicability: We describe our construction methodology in detail, so that + it can be verified and used by the scholarly communication community to create ongoing updates to our proposed statistics and indicators.
  • +
  • Readiness and timeliness: The methodology is built around well-established open databases + and already tested knowledge extraction technologies - natural language processing (NLP)/machine-learning (ML) - using operational + workflows in OpenAIRE to warrant timely results.
  • +
  • Trust and robustness: Our methodology also strives to be reliable, robust, and aligned + to other assessment methods so that it can be operationalized, used and reused, in conjunction with other assessment methods.
  • +
+
The text above is modified from this report (DOI: 10.2777/268348).
+
+
+

Step-by-step

+ +
+
+
+ ` +}) +export class MethodologyComponent implements OnInit, OnDestroy { + public stakeholder: Stakeholder; + public tab: 'entities' | 'attributes' = 'entities'; + private subscriptions: any[] = []; + public openaireEntities = OpenaireEntities; + + constructor(private stakeholderService: StakeholderService, + private seoService: SEOService, + private _meta: Meta, + private _router: Router, + private route: ActivatedRoute, + private _title: Title) { + } + + ngOnInit() { + this.subscriptions.push(this.stakeholderService.getStakeholderAsObservable().subscribe(stakeholder => { + this.stakeholder = stakeholder; + if (this.stakeholder) { + /* Metadata */ + const url = properties.domain + properties.baseLink + this._router.url; + this.seoService.createLinkForCanonicalURL(url, false); + this._meta.updateTag({content: url}, "property='og:url'"); + const description = "Methodology | " + this.stakeholder.name; + const title = "Methodology | " + this.stakeholder.name; + this._meta.updateTag({content: description}, "name='description'"); + this._meta.updateTag({content: description}, "property='og:description'"); + this._meta.updateTag({content: title}, "property='og:title'"); + this._title.setTitle(title); + } + })); + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + if (subscription instanceof Subscription) { + subscription.unsubscribe(); + } + }); + } +} diff --git a/monitor/methodology/methodology.module.ts b/monitor/methodology/methodology.module.ts new file mode 100644 index 00000000..f47f116d --- /dev/null +++ b/monitor/methodology/methodology.module.ts @@ -0,0 +1,28 @@ +import {NgModule} from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {MethodologyComponent} from "./methodology.component"; +import {RouterModule} from "@angular/router"; +import {PreviousRouteRecorder} from "../../utils/piwik/previousRouteRecorder.guard"; +import {PageContentModule} from "../../dashboard/sharedComponents/page-content/page-content.module"; +import {HowModule} from "../../monitor/how/how.module"; +import {TabsModule} from "../../utils/tabs/tabs.module"; +import {IconsModule} from "../../utils/icons/icons.module"; +import {IconsService} from "../../utils/icons/icons.service"; +import {graph} from "../../utils/icons/icons"; + +@NgModule({ + declarations: [MethodologyComponent], + imports: [CommonModule, RouterModule.forChild([ + { + path: '', + component: MethodologyComponent, + canDeactivate: [PreviousRouteRecorder] + }, + ]), PageContentModule, HowModule, TabsModule, IconsModule], + exports: [MethodologyComponent] +}) +export class MethodologyModule { + constructor(private iconsService: IconsService) { + this.iconsService.registerIcons([graph]); + } +} diff --git a/monitor/services/resources.service.ts b/monitor/services/resources.service.ts new file mode 100644 index 00000000..d51b9997 --- /dev/null +++ b/monitor/services/resources.service.ts @@ -0,0 +1,117 @@ +import {Injectable} from "@angular/core"; +import {MenuItem, RootMenuItem} from "../../sharedComponents/menu"; +import {Option} from "../../sharedComponents/input/input.component"; +import {StakeholderEntities} from "../entities/stakeholder"; +import {from, Subscription} from "rxjs"; +import {HttpClient} from "@angular/common/http"; +import {properties} from "../../../../environments/environment"; +import {Page} from "../../utils/entities/adminTool/page"; +import {map} from "rxjs/operators"; + +@Injectable({ + providedIn: 'root' +}) +export class ResourcesService { + + private subscription: Subscription; + private routes = ResourcesService.types.map(type => '/indicators/' + type.value); + + public static readonly types: Option[] = [ + {value: 'funder', label: StakeholderEntities.FUNDERS}, + {value: 'ri', label: StakeholderEntities.RIS}, + {value: 'project', label: StakeholderEntities.PROJECTS}, + {value: 'organization', label: StakeholderEntities.ORGANIZATIONS} + ]; + + constructor(private http: HttpClient) { + } + + private async getResourcesItemsAsync(prefix = '', portal: string = null): Promise { + let items = []; + let methodology = ResourcesService.setLink(new MenuItem("methodology", "Methodology", "", "", false, [], + null, {}, null, null, null, null, '_self'), + prefix + "/methodology", portal); + methodology.items = [ResourcesService.setLink(new MenuItem("methodology", "Terminology and construction", + "", "", false, [], null, {}, null, null, null, null, '_self'), + prefix + "/methodology", portal), + ResourcesService.setLink(new MenuItem("methodology", "See how it works", + "", "", false, [], null, {}, null, "how", null, null, '_self'), + prefix + "/methodology", portal)]; + items.push(methodology); + let indicators = new MenuItem("indicators-page", "Indicators", + "", "", false, [], null, {}, null, null, null, null, '_self'); + let promise = new Promise(resolve => { + this.isPagesEnabled().subscribe(status => { + ResourcesService.types.forEach((type, index) => { + if(status[index]) { + indicators.items.push(ResourcesService.setLink( + new MenuItem("indicators-" + type.value, type.label, + "", "", false, [], null, {}, null, null, null, null, '_self'), + prefix + "/indicators/" + type.value, portal) + ); + } + }); + resolve(); + }, error => { + console.error(error); + resolve(); + }); + }) + await promise; + if(indicators.items.length > 0) { + indicators.url = indicators.items[0].url; + indicators.route = indicators.items[0].route; + items.push(indicators); + } + return items; + } + + /** + * @deprecated + * */ + setResourcesDeprecated(items: RootMenuItem[], prefix = '', portal: string = null) { + if(this.subscription) { + this.subscription.unsubscribe(); + } + let resources = new MenuItem('resources', 'Resources', "", "", false, [], null, {}); + let index = items.push({rootItem: resources, items: []}) - 1; + this.subscription = from(this.getResourcesItemsAsync(prefix, portal)).subscribe(resourcesItems => { + items[index].items = resourcesItems; + }); + } + + setResources(items: MenuItem[], prefix = '', portal: string = null) { + if(this.subscription) { + this.subscription.unsubscribe(); + } + let resources = new MenuItem('resources', 'Resources', "", "", false, [], null, {}); + let index = items.push(resources) - 1; + this.subscription = from(this.getResourcesItemsAsync(prefix, portal)).subscribe(resourcesItems => { + items[index].items = resourcesItems; + }); + } + + public isPagesEnabled() { + let url = properties.adminToolsAPIURL + "/monitor/monitor/pages"; + return this.http.get((properties.useLongCache) ? (properties.cacheUrl + encodeURIComponent(url)) : url) + .pipe(map(pages => { + let result = this.routes.map(() => false); + pages.forEach(page => { + let index = this.routes.findIndex(route => route === page.route && page.isEnabled); + if(index !== -1) { + result[index] = true; + } + }); + return result; + })); + } + + private static setLink(item: MenuItem, route: string, portal: string = null) { + if(portal) { + item.url = portal + route + (item.fragment?'#' + item.fragment:''); + } else { + item.route = route; + } + return item; + } +} diff --git a/notifications/notification-user/notification-user.component.less b/notifications/notification-user/notification-user.component.less index 1a0bf5a4..8f68ec58 100644 --- a/notifications/notification-user/notification-user.component.less +++ b/notifications/notification-user/notification-user.component.less @@ -15,7 +15,7 @@ svg { &.outlined { circle { - ill: white; + fill: white; stroke: currentColor; } diff --git a/sharedComponents/navigationBar.component.html b/sharedComponents/navigationBar.component.html index 875e8a40..50a216fe 100644 --- a/sharedComponents/navigationBar.component.html +++ b/sharedComponents/navigationBar.component.html @@ -64,13 +64,13 @@ target="{{submenu.target}}">{{submenu.title}} @@ -160,12 +160,12 @@ target="{{submenu.target}}">{{submenu.title}} diff --git a/utils/theme/theme.component.html b/utils/theme/theme.component.html index 168f72fb..deb6c2c4 100644 --- a/utils/theme/theme.component.html +++ b/utils/theme/theme.component.html @@ -532,12 +532,12 @@
- + +