Compare commits

...

59 Commits

Author SHA1 Message Date
argirok c1a8151184 [develop | DONE | FIXED] linking disable link to unidentified for manage claims page 2024-05-10 11:34:17 +03:00
argirok c00403a24c [develop | DONE | FIXED] linking initialize properly claims properties when there are not pass as input 2024-05-10 11:33:42 +03:00
argirok 8a8b58eeaa [develop | DONE ADDED] add direct linking for organizations 2024-05-09 16:44:54 +03:00
Konstantinos Triantafyllou 6e96974364 Revert transition disable function 2024-05-01 15:59:48 +03:00
Alex Martzios c4b57e91ab [develop | DONE | CHANGED] move CustomRouteReuseStrategy to openaireLibrary 2024-05-01 11:25:12 +03:00
Konstantinos Triantafyllou b054a823da [develop]: Move OAIndicator in indicatorUtils file. 2024-04-30 17:15:15 +03:00
Konstantinos Triantafyllou b41357c4b8 [develop]: Add compose mail for develop personal-info 2024-04-30 17:09:23 +03:00
Konstantinos Triantafyllou fb34dd415e Merge branch 'develop' of code-repo.d4science.org:MaDgIK/openaire-library into develop 2024-04-30 15:38:39 +03:00
Konstantinos Triantafyllou bc2dd7d285 [develop]: Add open Access map in order to add configure open access indicator. 2024-04-30 15:38:31 +03:00
Konstantina Galouni 2127bfdf16 [develop | DONE | CHANGED]: Updated color of selected search filter labels (not purple, but portal color).
1. newSearchPage.component.html: Replaced class "uk-label-search-filter" with "uk-label-secondary" on selected search filter labels.
2. color.less: Removed color variable @search-filter-color: #B41FD0;
3. label.less: Removed custom color variables and class "uk-label-search-filter" | Updated class "uk-label-secondary".
4. explore-custom.less: Override @uk-label-secondary: @explore-color;
2024-04-30 13:08:54 +03:00
Konstantinos Triantafyllou 4231b65880 Merge branch 'develop' of code-repo.d4science.org:MaDgIK/openaire-library into develop 2024-04-25 15:11:13 +03:00
Konstantinos Triantafyllou 9b79d0c1e0 [develop]: Fix role users, role-verification type mapping. 2024-04-25 15:11:04 +03:00
Konstantina Galouni ac46ecb997 [develop | WIP | FIXED]: parsingFunctions.class.ts: Added check if there are no classifiedSubjects. 2024-04-24 16:35:13 +03:00
Konstantina Galouni c5bd710621 [develop | WIP | CHANGED]: [BETA] parsingFunctions.class.ts: Show all subjects as keywords - updated method "parseAllSubjects()" to add classified subjects (by vocabulary) to the end of the keyword subjects list. 2024-04-24 16:06:45 +03:00
Konstantinos Triantafyllou 267944103f [develop]: update link to develop in bottom. 2024-04-24 09:39:27 +03:00
Konstantina Galouni 2cd8059794 [develop | WIP | FIXED]: showAuthors.component.ts: In uk-dropdown set [attr.container]="'#modal-container'" to always be on top of every other part of the page. 2024-04-23 16:58:48 +03:00
Konstantina Galouni e74dc1b58f [develop | DONE | FIXED]: showAuthors.component.ts: In uk-dropdown set "pos: bottom-right" and [attr.container]="modal ? '#modal-container' : false" - copy to clipboard is not fully functional. 2024-04-23 16:01:47 +03:00
Konstantina Galouni ce87f0690d [develop | DONE | FIXED]: BUG fixes in orcid author modal | BUG fixes in search filters column.
1. mobile-dropdown.component.ts: Added makrForCheck() method call in "open()".
2. deletedByInference.component.ts: Added @Input() isMobile: boolean = false; & In <result-preview> set parameter [isMobile]="isMobile".
3. resultLanding.component.html: In mobile view, in <deletedByInference> set parameters [isMobile]="isMobile" and [modal]="alertModalDeletedByInferenceFS".
4. showAuthors.component.ts:
5. searchOrganizations.component.ts: [BUG FIX] In <new-search-page> set parameter [showRefine]="refineFields?.length > 0" to display filters or not.
6. newSearchPage.component.html: [BUG FIX] Updated checks for displaying refine filters column or not.
2024-04-22 17:14:46 +03:00
argirok 68125b3577 [develop | DONE | CHANGED] Claims: keep both shortname and funderName, in cards show fundername. For unidentified, hide code and link 2024-04-19 12:09:00 +03:00
argirok 474a73e2f7 [develop | DONE | CHANGED] indicator utils: user date range overrides the default range a chart may have 2024-04-18 15:08:17 +03:00
argirok afdf6bf964 [develop | DONE | CHANGED] in filters use "publication", "software", "dataset", "other" table instead of the result one 2024-04-18 11:08:41 +03:00
Konstantinos Triantafyllou 9e9332f1a3 Merge branch 'develop' of code-repo.d4science.org:MaDgIK/openaire-library into develop 2024-04-16 16:08:13 +03:00
Konstantinos Triantafyllou a2b1bb8f70 [develop | FIXED | DONE]: Multi indicators paths fix dropdowns 2024-04-16 16:08:05 +03:00
Konstantina Galouni 559220c77c [develop | DONE | CHANGED]: landing.module.ts: Removed TabPagingComponent from declarations and exports | deletedByInference.module.ts: Removed LandingModule from imports | resultLanding.module.ts: Import SearchTabModule | Deleted tabPaging.component.ts. 2024-04-16 13:25:54 +03:00
Konstantina Galouni 4d80f6f39c Merge remote-tracking branch 'origin/develop' into develop 2024-04-16 12:28:23 +03:00
Konstantina Galouni cc43504306 [develop | DONE | ADDED]: Added method get calcCurrentMonth() to calculate and display current month of the project life, if status is on going (project has started and not yet ended). 2024-04-16 12:28:14 +03:00
Konstantinos Triantafyllou b729341199 [develop]: Fix visibility condition in Indicaotrs page 2024-04-16 12:21:02 +03:00
Konstantina Galouni eeb63a2de9 [develop | DONE | CHANGED]: In search projects results, show funder name by default, and if not available, show funder short name.
1. searchResult.ts: Added field funderName: string; in class SearchResult.
2. result-preview.ts: Added field funderName: string; in class ResultPreview & in method "searchResultConvert()" set resultPreview.funderName = result.funderName;
3. searchProjects.service.ts: Added parsing of funderName.
4. result-preview.component.html: Show funderName and if not available, show funderShortname.
5. entity-metadata.component.ts: Added check for funders display.
2024-04-16 11:47:12 +03:00
Konstantina Galouni 4b3e805d31 [develop | DONE | REVERTED]: searchProjects.service.ts: Revert parsing funder name if funder shortname is not available. 2024-04-16 11:36:45 +03:00
Konstantina Galouni c219c8370b [develop | DONE | REVERTED]: searchResult.component.ts & result-preview.component.html & result-preview.component.ts: Reverted changes regarding changeDetection: ChangeDetectionStrategy.OnPush - should be first introduced in another branch. 2024-04-16 10:07:31 +03:00
Konstantina Galouni 6886df2caa [develop | DONE | CHANGED]: Alt attribute on images | Set changeDetection: ChangeDetectionStrategy.OnPush on result-preview.component.ts.
1. deletedByInference.service.ts: [Bug fix] Fixed author check.
2. sdg.component.html & quick-contact.component.html & fos.component.html: Added alt attribute on images.
3. result-preview.component.ts: Set changeDetection: ChangeDetectionStrategy.OnPush | Added get countDots().
4. result-preview.component.html: Changed how number of access routes dots is passed into a class (countDots) - needed for ChangeDetectionStrategy.OnPush.
5. orcid-work.component.ts: Added alt attribute on images | Added ChangeDetectorRef and call this.cdr.markForCheck(); on "getPutCode()", "saveWork()" and "deleteWorks()" - needed for ChangeDetectionStrategy.OnPush.
6. searchResult.component.ts: Rebuild previewResults object after getting orcidPutCodes to draw html again - needed for ChangeDetectionStrategy.OnPush.
7. newSearchPage.component.html: Removed uk-flex-wrap for number of results and uk-margin-medium-left from desktop sorting.
2024-04-15 17:11:38 +03:00
Alex Martzios 9b04260a60 [develop | DONE | FIXED] showAuthors: fix z-index bug for ocrid dropdown inside modal 2024-04-15 12:09:35 +03:00
Alex Martzios 6d9d118d57 [develop | DONE | ADDED] topics/indicators: add condition to hide all "edit" actions when stakeholder is NOT a copy 2024-04-12 13:57:56 +03:00
Konstantina Galouni c7817731f0 Merge remote-tracking branch 'origin/develop' into develop 2024-04-12 11:19:33 +03:00
Konstantina Galouni 438075f8df [develop | DONE | FIXED]: Fixes on search projects for claims.
1. searchProjects.service.ts: Parse funder name if funder shortname is not available.
2. claimProjectSearchForm.component.ts:
   a. [BUG FIX] Set filter.countAllValues = filter.values.length; to show filter values without extra "view all" call
   b. [BUG FIX] Call projectService.advancedSearchProjects instead of projectService.searchProjects to get projects (/resources2 instead of /projects) to get 100 first values by default in facets.
2024-04-12 11:19:17 +03:00
Konstantinos Triantafyllou 8f4e653d50 [develop | WIP]: Introduce copy field in stakeholder 2024-04-12 11:14:26 +03:00
Alex Martzios c4180c7aa2 [develop | DONE | CHANGED] search page: change the color or customFilter pills as well 2024-04-11 16:46:58 +03:00
Alex Martzios 31e61d55a0 [develop | DONE | CHANGED] search page: change the coloring of the selected filter pills, based on the new design mocks 2024-04-11 16:34:13 +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
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
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
70 changed files with 1716 additions and 1111 deletions

View File

@ -28,6 +28,7 @@ export class ClaimResult {
export class ClaimProject {
public funderId: string;
public funderShortname: string;
public funderName: string;
public acronym: string;
public startDate: string;
@ -37,6 +38,12 @@ export class ClaimProject {
public fundingLevel0: string;
public url: string;
}
export class ClaimOrganization {
public name: string;
// public shortName: string;
public url: string;
// public country: string;
}
export class ClaimContext {
public community: string;
@ -71,6 +78,7 @@ export class ClaimEntity {
result: ClaimResult;
project: ClaimProject;
context: ClaimContext;
organization: ClaimOrganization;
constructor() {
this.warningMessages = [];
@ -92,6 +100,7 @@ export class ClaimRecord2Insert {
targetAccessRights: string;
targetEmbargoEndDate: string;
claimedInDashboard: string;
idSuffix:string;
constructor() {
@ -115,7 +124,12 @@ export class ClaimDBContext {
title: string;
openaireId: string;
}
export class ClaimDBOrganization {
openaireId: string;
name: string;
shortName: string;
country: string;
}
export class ClaimDBProject {
openaireId: string;
name: string;

View File

@ -65,32 +65,33 @@ export class ClaimProjectsSearchFormComponent {
}
search(page,size) {
if(this.keyword.length == 0){
this.showResults =false;
if (this.keyword.length == 0) {
this.showResults = false;
return;
}
this.showResults =true;
this.showResults = true;
this.openaireResults = [];
this.openaireResultsStatus = this.errorCodes.LOADING;
this.prevFilters = this.filters;
//searchProjects (params: string, refineParams:string, page: number, size: number, refineFields:string[] , properties:EnvProperties ):any {
this.sub = this._projectService.searchProjects(this.createOpenaireQueryParams(),(page==1)? this.refineFieldsQuery:null, page, size, (page==1)?this.refineFields:[], this.properties).subscribe(
this.sub = this._projectService.advancedSearchProjects(this.createOpenaireQueryParams(), page, size, this.properties, (page == 1) ? this.refineFieldsQuery : null, (page == 1) ? this.refineFields : [], this.createOpenaireRefineQuery()).subscribe(
// this.sub = this._projectService.searchProjects(this.createOpenaireQueryParams(),(page==1)? this.refineFieldsQuery:null, page, size, (page==1)?this.refineFields:[], this.properties).subscribe(
data => {
if(data != null) {
this.openaireResultsPage=page;
if (data != null) {
this.openaireResultsPage = page;
this.openaireResultsNum = data[0];
this.openaireResults =ClaimProjectsSearchFormComponent.openaire2ClaimEntity(data[1], this.properties);
if(data[2] && data[2].length > 0){
this.filters = this.checkSelectedFilters( data[2], this.prevFilters);
this.openaireResults = ClaimProjectsSearchFormComponent.openaire2ClaimEntity(data[1], this.properties);
if (data[2] && data[2].length > 0) {
this.filters = this.checkSelectedFilters(data[2], this.prevFilters);
}
this.openaireResultsStatus = this.errorCodes.DONE;
if(this.openaireResultsNum == 0){
if (this.openaireResultsNum == 0) {
this.openaireResultsStatus = this.errorCodes.NONE;
this.filters = this.checkSelectedFilters( [], this.prevFilters);
this.filters = this.checkSelectedFilters([], this.prevFilters);
}
}else {
} else {
this.openaireResultsStatus = this.errorCodes.ERROR;
}
},
@ -177,9 +178,10 @@ export class ClaimProjectsSearchFormComponent {
const entity: ClaimEntity = new ClaimEntity();
entity.project = new ClaimProject();
entity.project.funderId = item.funderId;
entity.project.funderName = item.funderShortname;
entity.project.funderShortname = item.funderShortname?item.funderShortname:(entity.project.funderId.split("::")[1]);
entity.project.funderName = item.funderName;
entity.id = item.id;
entity.project.url = properties.searchLinkToProject + entity.id;
entity.project.url = (item.code !="unidentified") ? properties.searchLinkToProject + entity.id : null;
entity.title = item.title.name;
entity.project.acronym = item.acronym;
entity.project.startDate = item.startYear;
@ -202,12 +204,16 @@ export class ClaimProjectsSearchFormComponent {
}
createOpenaireQueryParams():string {
createOpenaireQueryParams(): string {
let query = "";
if(this.keyword.length > 0){
query += "q=" + StringUtils.quote(StringUtils.URIEncode(this.keyword));
if (this.keyword.length > 0) {
// query += "q=" + StringUtils.quote(StringUtils.URIEncode(this.keyword));
query += StringUtils.quote(StringUtils.URIEncode(this.keyword));
}
return query;
}
createOpenaireRefineQuery(): string {
/*if(this.startYear.length > 0 ){
query+='&fq=projectstartyear exact \"'+this.startYear+'\"'
}
@ -215,30 +221,30 @@ export class ClaimProjectsSearchFormComponent {
query+='&fq=projectendyear exact \"'+this.endYear+'\"'
}*/
let allFqs = "";
for (let filter of this.filters){
if(filter.countSelectedValues > 0){
let count_selected=0;
for (let filter of this.filters) {
if (filter.countSelectedValues > 0) {
let count_selected = 0;
let fq = "";
for (let value of filter.values){
if(value.selected == true){
for (let value of filter.values) {
if (value.selected == true) {
count_selected++;
fq+=(fq.length > 0 ? " " + filter.filterOperator + " ":"" ) + filter.filterId + " exact " + (StringUtils.quote(value.id));
fq += (fq.length > 0 ? " " + filter.filterOperator + " " : "") + filter.filterId + " exact " + (StringUtils.quote(value.id));
}
}
if(count_selected > 0){
fq="&fq="+StringUtils.URIEncode(fq);
if (count_selected > 0) {
fq = "&fq=" + StringUtils.URIEncode(fq);
allFqs += fq;
}
}
}
for (let i=0; i<this.rangeFilters.length; i++){
for (let i = 0; i < this.rangeFilters.length; i++) {
let filter = this.rangeFilters[i];
//selectedFromValue, selectedToValue, equalityOp, equalityOpFrom, equalityOpTo, filterOp ){
allFqs+= NewSearchPageComponent.createRangeFilterQuery(this.rangeFields[i],filter.selectedFromValue, filter.selectedToValue, " within ", ">=" ,"<=", "and" )
allFqs += NewSearchPageComponent.createRangeFilterQuery(this.rangeFields[i], filter.selectedFromValue, filter.selectedToValue, " within ", ">=", "<=", "and")
}
return allFqs + "&type=projects";
}
return query+allFqs;
}
public yearChanged() {
this.search(this.page, this.size);
@ -273,18 +279,19 @@ export class ClaimProjectsSearchFormComponent {
}
}
filter.countAllValues = filter.values.length;
}
if(filters.length == 0 ){
for(let j=0; j< prevFilters.length ; j++){
for(let j=0; j< prevFilters.length ; j++) {
let filter = Object.assign({}, prevFilters[j]);
filter.values = [];
for(let filterValue of prevFilters[j].values) {
if(filterValue.selected){
for (let filterValue of prevFilters[j].values) {
if (filterValue.selected) {
filterValue.number = 0;
filter.values.push(filterValue);
}
}
filter.countAllValues = filter.values.length;
filters.push(filter)
}
}

View File

@ -0,0 +1,20 @@
export class ClaimsProperties{
ALLOW_ORGANIZATION_LINKING:boolean = false;
INLINE_ENTITY = {
show: true,
guideText : null
}
BASKET ={
source_title: "Source",
target_title: "Link source to"
}
METADATA_PREVIEW ={
source_title: "Source",
target_title: "Link to",
edit_source_title: "Edit",
edit_target_title: "Edit",
edit_target_icon: "edit"
}
}

View File

@ -1,11 +1,11 @@
<schema2jsonld *ngIf="url" [URL]="url" [name]="pageTitle" type="other"></schema2jsonld>
<div class="uk-grid">
<div class="uk-width-expand uk-position-relative">
<div class="uk-width-expand uk-position-relative uk-margin-small-top">
<helper *ngIf="pageContents && pageContents['top'] && pageContents['top'].length > 0"
[texts]="pageContents['top']"></helper>
<div *ngIf="filterForm" [id]="actions?'page_content_actions':null" [class.uk-blur-background]="actions" [attr.uk-sticky]="(actions)?'media: @m':null" [attr.offset]="offset">
<div [class.uk-padding-small]="actions" class="uk-padding-remove-vertical">
<div class="uk-section-xsmall">
<div class="uk-flex uk-flex-right@m uk-flex-center uk-flex-wrap uk-flex-middle">
<div search-input [disabled]="loading" [searchControl]="filterForm.controls.keyword" searchInputClass="outer"
placeholder="Search links" class="uk-width-1-3@xl uk-width-2-5@l uk-width-1-2@m uk-width-1-1"
@ -13,13 +13,15 @@
</div>
</div>
</div>
</div>
<div [class.uk-padding]="actions" class="uk-padding-remove-vertical">
<div class="uk-margin-medium-top">
<div class="uk-margin-small-top">
<results-and-pages [type]="resultsNum !== 1?'Links':'Link'" [page]="page" [pageSize]="size" [hasSearch]="true" [searchTerm]="keyword"
[totalResults]="resultsNum" customClasses="uk-margin-remove"></results-and-pages>
</div>
<div class="uk-grid uk-flex-middle uk-margin-medium-top" uk-grid>
<div class="uk-grid uk-flex-middle uk-margin-small-top" uk-grid>
<div *ngIf="fetchBy != 'User' && properties.environment == 'development'" >
<input [(ngModel)]="mine" [checked]="mine" (ngModelChange)="goTo(1)" type="checkbox" class="uk-checkbox"> Mine
</div>
<div>
<dropdown-filter #dropdownFilter dropdownClass="uk-width-medium uk-padding-small"
name="Type of Entity" [disabled]="loading" [count]="entities.length">
@ -43,6 +45,7 @@
type="select" [options]="sortOptions">
</div>
</div>
<div class="uk-width-expand@l uk-width-1-1 uk-flex uk-flex-right@m uk-flex-center">
<paging-no-load [currentPage]="page" [totalResults]="resultsNum" [size]="size"
[loading]="false" (pageChange)="pageChange($event)"></paging-no-load>

View File

@ -40,7 +40,7 @@ export class DisplayClaimsComponent implements OnInit, OnDestroy {
page: number = 1;
size: number = 50;
keyword: string; // the keyword string to give to the request as parameter
types = ["All", "Project", "Context", "Result", "User"];
types = ["All", "Project", "Context", "Result", "User", "Organization"];
loading: boolean = false;
@Input() fetchBy: string;
@Input() fetchId: string;
@ -53,6 +53,7 @@ export class DisplayClaimsComponent implements OnInit, OnDestroy {
public filterForm: FormGroup;
public entities: string[] = [];
selected = [];
mine = false;
allOptions: Option[] = [
{label: OpenaireEntities.PUBLICATIONS, value: "publication"},
@ -133,6 +134,7 @@ export class DisplayClaimsComponent implements OnInit, OnDestroy {
this.fetchId = this.fetchId ? this.fetchId : '';
}
let page = (params['page'] === undefined) ? 1 : +params['page'];
this.mine = (params['mine'] == 'true' ? true:false);
this.keyword = (params['keyword'] ? params['keyword'] : "");
this.filterForm.get('keyword').setValue(this.keyword);
this.page = (page <= 0) ? 1 : page;
@ -186,6 +188,16 @@ export class DisplayClaimsComponent implements OnInit, OnDestroy {
this.handleErrors(err, "Error getting claims for project with id: " + this.fetchId);
}
);
} else if (this.fetchBy == "Organization") {
this.subResults = this._claimService.getClaimsByOrganization(this.size, this.page, this.fetchId, this.keyword, this.filterForm.get("sort").value.sort, this.filterForm.get("sort").value.descending, types, this.properties.claimsAPIURL, this.mine).subscribe(
data => {
this.manageAPIData(data);
this.loading = false;
},
err => {
this.handleErrors(err, "Error getting claims for project with id: " + this.fetchId);
}
);
} else if (this.fetchBy == "User") {
this.subResults = this._claimService.getClaimsByUser(this.size, this.page, this.fetchId, this.keyword, this.filterForm.get("sort").value.sort, this.filterForm.get("sort").value.descending, types, this.properties.claimsAPIURL).subscribe(
data => {
@ -209,7 +221,7 @@ export class DisplayClaimsComponent implements OnInit, OnDestroy {
}
);
} else if (this.fetchBy == "Context") {
this.subResults = this._claimService.getClaimsBycontext(this.size, this.page, this.fetchId, this.keyword, this.filterForm.get("sort").value.sort, this.filterForm.get("sort").value.descending, types, this.properties.claimsAPIURL).subscribe(
this.subResults = this._claimService.getClaimsBycontext(this.size, this.page, this.fetchId, this.keyword, this.filterForm.get("sort").value.sort, this.filterForm.get("sort").value.descending, types, this.properties.claimsAPIURL, this.mine).subscribe(
data => {
this.manageAPIData(data);
this.loading = false;
@ -220,7 +232,7 @@ export class DisplayClaimsComponent implements OnInit, OnDestroy {
}
);
} else {
this.subResults = this._claimService.getClaims(this.size, this.page, this.keyword, this.filterForm.get("sort").value.sort, this.filterForm.get("sort").value.descending, types, this.properties.claimsAPIURL).subscribe(
this.subResults = this._claimService.getClaims(this.size, this.page, this.keyword, this.filterForm.get("sort").value.sort, this.filterForm.get("sort").value.descending, types, this.properties.claimsAPIURL, this.mine).subscribe(
data => {
this.manageAPIData(data);
this.loading = false;
@ -259,15 +271,8 @@ export class DisplayClaimsComponent implements OnInit, OnDestroy {
types += (types.length > 0 ? ',' : '') + type;
}
params += (this.entities.length > 0) ? (params.length > 0 ? '&' : '') + "types=" + types : "";
if (this.isAdmin) {
params += (this.fetchBy == 'All' ? "" : (params.length > 0 ? '&' : '') + "fetchBy=" + this.fetchBy);
params += (this.fetchId == '' ? "" : (params.length > 0 ? '&' : '') + "fetchId=" + this.fetchId);
}
params += (this.filterForm.get("sort").value.sort == 'date' && this.filterForm.get("sort").value.descending ? "" : (params.length > 0 ? '&' : '') + "sort=" + this.filterForm.get("sort").value.sort + "-" + this.filterForm.get("sort").value.descending);
params += (this.keyword == '' ? "" : (params.length > 0 ? '&' : '') + "keyword=" + this.keyword);
if (this.communityId != null) {
params += "&communityId=" + this.communityId;
}
return params;
}
@ -364,7 +369,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

@ -1,6 +1,6 @@
import {Component, Input} from '@angular/core';
import {EnvProperties} from '../../../utils/properties/env-properties';
import {ClaimDBContext, ClaimDBProject, ClaimDBResult} from "../claimHelper.class";
import {ClaimDBContext, ClaimDBOrganization, ClaimDBProject, ClaimDBResult} from "../claimHelper.class";
import {OpenaireEntities} from "../../../utils/properties/searchFields";
import {StringUtils} from "../../../utils/string-utils.class";
@ -26,6 +26,10 @@ import {StringUtils} from "../../../utils/string-utils.class";
<project-title [project]="entity" [searchLink]=properties.searchLinkToProject
[externalPortalUrl]=externalPortalUrl></project-title>
</div>
<div *ngIf="type == 'organization'" [attr.uk-tooptip]="getEntityName(type)">
<span class="uk-text-meta uk-text-small uk-margin-small-right uk-text-nowrap">Link to:</span>
<organization-title [organization]="entity"></organization-title>
</div>
<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">
@ -36,7 +40,7 @@ import {StringUtils} from "../../../utils/string-utils.class";
})
export class ClaimEntityFormatter {
@Input() entity: ClaimDBResult | ClaimDBContext | ClaimDBProject;
@Input() entity: ClaimDBResult | ClaimDBContext | ClaimDBProject | ClaimDBOrganization;
@Input() type: string;
@Input() properties: EnvProperties;
@Input() externalPortalUrl: string = null;

View File

@ -5,18 +5,19 @@ import { RouterModule } from '@angular/router';
import {ProjectTitleFormatter} from './projectTitleFormatter.component';
import {PublicationTitleFormatter} from './publicationTitleFormatter.component';
import {ClaimEntityFormatter} from './claimEntityFormatter.component';
import {OrganizationTitleFormatterComponent} from "./organizationTitleFormatter.component";
@NgModule({
imports: [
CommonModule, RouterModule
],
declarations: [
ProjectTitleFormatter, PublicationTitleFormatter, ClaimEntityFormatter
ProjectTitleFormatter, PublicationTitleFormatter, ClaimEntityFormatter, OrganizationTitleFormatterComponent
],
providers: [ ],
exports: [
ProjectTitleFormatter, PublicationTitleFormatter, ClaimEntityFormatter
ProjectTitleFormatter, PublicationTitleFormatter, ClaimEntityFormatter, OrganizationTitleFormatterComponent
]
})

View File

@ -0,0 +1,18 @@
import {Component, Input} from '@angular/core';
@Component({
selector: 'organization-title',
template: `
<h6 *ngIf="organization" class="uk-margin-remove multi-line-ellipsis lines-2">
<p class="uk-margin-remove">
{{(organization.shortName ? ('[' + organization.shortName + '] ') : '')}}{{organization.name}}
</p>
</h6>
`
})
export class OrganizationTitleFormatterComponent {
@Input() organization: any;
public url: string;
}

View File

@ -10,10 +10,10 @@ import {properties} from "../../../../../environments/environment";
<ng-container>
<h6 class="uk-margin-remove multi-line-ellipsis lines-2">
<p class="uk-margin-remove">
<a *ngIf="externalPortalUrl" [href]="externalPortalUrl + projectUrl + '?projectId='+project['openaireId']" class="uk-link uk-link-heading">
<a *ngIf="externalPortalUrl" [href]="externalPortalUrl + projectUrl + '?projectId='+project['openaireId']" class="uk-link uk-link-heading" [class.uk-disabled]="project.name == 'unidentified'">
{{(project['acronym'] ? ('[' + project['acronym'] + '] ') : '')}}{{project['name']}}
</a>
<a *ngIf="!externalPortalUrl" [routerLink]="projectUrl" [queryParams]="routerHelper.createQueryParam('projectId',project['openaireId'])" class="uk-link uk-link-heading">
<a *ngIf="!externalPortalUrl" [routerLink]="projectUrl" [queryParams]="routerHelper.createQueryParam('projectId',project['openaireId'])" class="uk-link uk-link-heading" [class.uk-disabled]="project.name == 'unidentified'">
{{(project['acronym'] ? ('[' + project['acronym'] + '] ') : '')}}{{project['name']}}
</a>
</p>

View File

@ -12,8 +12,8 @@ export class ClaimsService {
private getClaimRequest(size : number, page : number, url :string, fromCache:boolean):any {
return this.http.get(url, CustomOptions.getAuthOptionsWithBody());
}
getClaims( size : number, page : number, keyword:string, sortby: string, descending: boolean, types: string, apiUrl:string):any {
let url = apiUrl +"claims"+"?offset="+(size*(page-1) + "&limit="+size)+"&keyword="+keyword+"&sortby="+sortby+"&descending="+descending+(types.length>0?"&"+types:types);
getClaims( size : number, page : number, keyword:string, sortby: string, descending: boolean, types: string, apiUrl:string, mine:boolean):any {
let url = apiUrl +"claims"+"?offset="+(size*(page-1) + "&limit="+size)+"&keyword="+keyword+"&sortby="+sortby+"&descending="+descending+(types.length>0?"&"+types:types) + (mine?"&mine=true":"") + (mine?"&mine=true":"");
return this.getClaimRequest(size,page,url,true);
}
@ -23,7 +23,7 @@ export class ClaimsService {
return this.getClaimRequest(size,page,url,false);
}
getClaimsBycontext( size : number, page : number, contextId:string, keyword:string, sortby: string, descending: boolean, types: string , apiUrl:string):any {
getClaimsBycontext( size : number, page : number, contextId:string, keyword:string, sortby: string, descending: boolean, types: string , apiUrl:string, mine:boolean):any {
//console.info('ClaimsService: getClaims for context : '+contextId);
let url = apiUrl +"contexts/"+contextId+"/claims"+"?offset="+(size*(page-1) + "&limit="+size)+"&keyword="+keyword+"&sortby="+sortby+"&descending="+descending+(types.length>0?"&"+types:types);
return this.getClaimRequest(size,page,url,true);
@ -40,6 +40,10 @@ export class ClaimsService {
let url = apiUrl +"projects/"+projectId+"/claims"+"?offset="+(size*(page-1) + "&limit="+size)+"&keyword="+keyword+"&sortby="+sortby+"&descending="+descending+(types.length>0?"&"+types:types);
return this.getClaimRequest(size,page,url,true);
}
getClaimsByOrganization( size : number, page : number, organizationId:string, keyword:string, sortby: string, descending: boolean, types: string, apiUrl:string, mine:boolean):any {
let url = apiUrl +"organizations/"+organizationId+"/claims"+"?offset="+(size*(page-1) + "&limit="+size)+"&keyword="+keyword+"&sortby="+sortby+"&descending="+descending+(types.length>0?"&"+types:types) + (mine?"&mine=true":"");
return this.getClaimRequest(size,page,url,true);
}
deleteClaimById(claimId:string , apiUrl:string):any{
//console.warn('Trying to delete claim with id : '+claimId);

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

@ -1,6 +1,6 @@
<linking-generic *ngIf="validInput" [localStoragePrefix]="localStoragePrefix" [results]="results" [sources]="sources"
[communityId]="communityId" [inlineEntity]="inlineEntity" [showOptions]="showOptions"
pageTitle="Direct Linking">
pageTitle="Direct Linking" [claimsProperties]="claimsProperties">
</linking-generic>
<div *ngIf="validInput== false" id="tm-main" class=" uk-section uk-padding-remove-top tm-middle">

View File

@ -1,7 +1,7 @@
import {Component, Input, ViewChild} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {EnvProperties} from '../../utils/properties/env-properties';
import {ClaimEntity, ClaimProject, ShowOptions} from '../claim-utils/claimHelper.class';
import {ClaimEntity, ClaimOrganization, ClaimProject, ShowOptions} from '../claim-utils/claimHelper.class';
import {EntitiesSearchService} from '../../utils/entitiesAutoComplete/entitySearch.service';
import {SearchResearchResultsService} from '../../services/searchResearchResults.service';
import {LinkingGenericComponent} from "../linking/linkingGeneric.component";
@ -11,6 +11,7 @@ import {properties} from "../../../../environments/environment";
import {OpenaireEntities} from "../../utils/properties/searchFields";
import {StringUtils} from "../../utils/string-utils.class";
import {ClaimProjectsSearchFormComponent} from "../claim-utils/claimProjectSearchForm.component";
import {ClaimsProperties} from "../claim-utils/claims.properties";
@Component({
@ -24,18 +25,20 @@ export class DirectLinkingComponent {
// linkType: string = "project"; // link type (selected in home page) : project, context, software, etc
/* url Parameters for inline linking */
id: string = null; //entity id
@Input() id: string = null; //entity id
type: string = null; // entity type (publication or dataset)
// linkTo: string = null; // entity type (project or context or entity)
// linkToEntities: string[] = [];
showOptions:ShowOptions = new ShowOptions();
validEntityTypes = ["dataset", "publication", "software", "other", "project", "context"];
validEntityTypes = ["dataset", "publication", "software", "other", "project", "context", "organization"];
sources: ClaimEntity[] = [];
inlineEntity: ClaimEntity = null;
validInput: boolean = null;//'true;
properties: EnvProperties;
@Input() communityId: string = null;
localStoragePrefix: string = "";
@Input() organizationClaim: boolean = false;
@Input() claimsProperties:ClaimsProperties = new ClaimsProperties();
constructor(private _router: Router, private route: ActivatedRoute,private entitySearch:EntitiesSearchService,
private _searchResearchResultsService: SearchResearchResultsService) {}
subscriptions = [];
@ -48,11 +51,18 @@ export class DirectLinkingComponent {
}
ngOnInit() {
this.properties = properties;
/* if(!this.claimsProperties){
this.claimsProperties = new ClaimsProperties();
}*/
this.subscriptions.push(this.route.queryParams.subscribe(params => {
if(this.organizationClaim){
this.type = "organization";
this.showOptions.linkTo = "result";
}else {
this.id = params['id'];
this.type = params['type'];
this.showOptions.linkTo = params['linkTo'];
}
if (this.type != null && this.showOptions.linkTo != null) {
this.type = (this.validEntityTypes.indexOf(this.type) != -1) ? this.type : 'publication';
this.showOptions.linkTo = (this.validEntityTypes.indexOf(this.showOptions.linkTo) != -1 || this.showOptions.linkTo == "result") ? this.showOptions.linkTo : 'project';
@ -68,6 +78,8 @@ export class DirectLinkingComponent {
if (this.type == "project") {
// this.linkType = "project";
this.getProjectById(this.id);
} else if (this.type == "organization") {
this.getOrganizationById(this.id);
} else if (this.type == "publication") {
this.getResearchResultById("publication", this.id);
} else if (this.type == "dataset") {
@ -81,7 +93,7 @@ export class DirectLinkingComponent {
}
//set which entities it is allowed to link to.
// add first the
if(this.type == "project"){
if(this.type == "project" || this.type == "organization"){
this.showOptions.linkToEntities = ["result"];
this.showOptions.linkTo = "result";
}else{
@ -103,16 +115,19 @@ export class DirectLinkingComponent {
}
}));
}
isValidInput(result: ClaimEntity) {
if (result == null) {
return false;
} else if (this.type == "project" && this.showOptions.linkTo != "result") {
} else if (this.type == "organization" && !this.claimsProperties.ALLOW_ORGANIZATION_LINKING) {
return false;
} else if ((this.type == "project" || this.type == "organization") && this.showOptions.linkTo != "result") {
return false;
} else if (["dataset", "publication", "software", "other"].indexOf(this.type) != -1 && (["project", "context", "result"].indexOf(this.showOptions.linkTo) == -1)) {
return false;
} else if (["project", "dataset", "publication", "software", "other"].indexOf(this.type) == -1) {
} else if (["project", "dataset", "publication", "software", "other", "organization"].indexOf(this.type) == -1) {
return false;
} else {
return true;
@ -130,6 +145,17 @@ export class DirectLinkingComponent {
this.handleError("Error getting project by id: " + id, err);
}));
}
getOrganizationById(id: string) {
this.subscriptions.push(this.entitySearch.fetchByType(id,"organization", this.properties).subscribe(
data => {
this.createClaimEntity(data, "organization");
},
err => {
this.validInput = this.isValidInput(null);
//console.log("An error occured")
this.handleError("Error getting project by id: " + id, err);
}));
}
getResearchResultById(resultType: string, id: string) {
this.subscriptions.push(this._searchResearchResultsService.searchById(resultType, id, this.properties).subscribe(data => {
@ -156,11 +182,22 @@ export class DirectLinkingComponent {
entity.project.code = project.code;
entity.project.endDate = project.endDate;
entity.project.funderId = project.funderId;
entity.project.funderShortname = project.funderShortName?project.funderShortName:(entity.project.funderId.split("::")[1]);
entity.project.funderName = project.funderName;
entity.project.fundingLevel0 = project.fundingLevel0;
entity.project.jurisdiction = project.jurisdiction;
entity.project.startDate = project.startDate;
this.inlineEntity = entity;
}else if(type =="organization"){
let organization = data[0];
let entity:ClaimEntity = new ClaimEntity();
entity.id = organization.id;
entity.type = "organization";
entity.title = organization.label;
entity.organization = new ClaimOrganization();
// entity.organization.url = properties.searchLinkToOrganization + entity.id;
entity.organization.name = organization.label;
this.inlineEntity = entity;
}else{
results = ClaimResultSearchFormComponent.openaire2ClaimResults(data, this.properties);
}

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) {
} else if (this.inlineEntity && this.inlineEntity.type != "organization") {
directclaims.push({
"id": entity.id,
"record": ClaimInsertComponent.createDirectClaim(entity, [this.inlineEntity])
"record": ClaimInsertComponent.createDirectClaim(entity, [this.inlineEntity], idSuffix)
});
}
@ -187,11 +188,13 @@ 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));
/* } else if (entity.organization) {
claims.push(ClaimInsertComponent.createOrganizationClaim(result, entity, user.email, dashboard, idSuffix));*/
}
this.infoToLog.push([ result.title?result.title: result.id, entity.title?entity.title:entity.id]);
@ -201,15 +204,19 @@ 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));
}
} else if (this.inlineEntity.organization) {
if (entity.result) {
claims.push(ClaimInsertComponent.createOrganizationClaim(entity, this.inlineEntity, user.email, dashboard, idSuffix));
}
}
}
@ -384,13 +391,18 @@ export class ClaimInsertComponent {
localStorage.removeItem(this.localStoragePrefix + "results");
localStorage.removeItem(this.localStoragePrefix + "claimsJob");
localStorage.removeItem(this.localStoragePrefix + "feedRecordsJob");
if(this.properties.myClaimsLink && this.properties.myClaimsLink.indexOf(".") == -1) {
this._router.navigate([this.properties.myClaimsLink], {queryParams: this.params});
}else if(this.properties.myClaimsLink && this.properties.myClaimsLink.indexOf(".") != -1) {
this._router.navigate([this.properties.myClaimsLink], {relativeTo: this.route});
}else{
this._router.navigate(["/"]);
}
}
}
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 +415,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 +433,29 @@ export class ClaimInsertComponent {
targetCollectedFrom: resultEntity.result.source,
targetAccessRights: resultEntity.result.accessRights,
targetEmbargoEndDate: ClaimInsertComponent.getEmbargoEndDate(resultEntity),
claimedInDashboard : dashboard
claimedInDashboard : dashboard,
idSuffix : idSuffix
};
}
private static createOrganizationClaim(resultEntity: ClaimEntity, organizationEntity: ClaimEntity, user: any, dashboard:string, idSuffix:string): ClaimRecord2Insert {
return {
claimedBy: user,
sourceId: organizationEntity.id,
sourceType: "organization",
sourceCollectedFrom: "openaire",
sourceAccessRights: "OPEN",
sourceEmbargoEndDate: "",
targetId: resultEntity.id,
targetType: resultEntity.type,
targetCollectedFrom: resultEntity.result.source,
targetAccessRights: resultEntity.result.accessRights,
targetEmbargoEndDate: ClaimInsertComponent.getEmbargoEndDate(resultEntity),
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 +469,9 @@ export class ClaimInsertComponent {
targetCollectedFrom: inlineResult.result.source,
targetAccessRights: inlineResult.result.accessRights,
targetEmbargoEndDate: ClaimInsertComponent.getEmbargoEndDate(inlineResult),
claimedInDashboard : dashboard
claimedInDashboard : dashboard,
idSuffix : idSuffix
};
}
@ -448,12 +481,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;
@ -502,7 +536,7 @@ export class ClaimInsertComponent {
entity["linksToProjects"] = [];
}
let project: ClaimEntity = results[i];
entity["linksToProjects"].push("info:eu-repo/grantAgreement/" + project.project.funderName + "/" + project.project.fundingLevel0 + "/" + project.project.code + "/" + project.project.jurisdiction + "/" + project.title + "/" + project.project.acronym);
entity["linksToProjects"].push("info:eu-repo/grantAgreement/" + project.project.funderShortname + "/" + project.project.fundingLevel0 + "/" + project.project.code + "/" + project.project.jurisdiction + "/" + project.title + "/" + project.project.acronym);
} else if (results[i].context) {

View File

@ -67,18 +67,19 @@
[results]="results" [sources]="sources"
[localStoragePrefix]="localStoragePrefix" [inlineEntity]="inlineEntity"
[showOptions]="showOptions" [properties]=properties [pageContents]="pageContents"
[defaultColors]="!communityId" [communityId]="communityId"
[defaultColors]="!communityId" [communityId]="communityId" [claimsProperties]="claimsProperties"
>
</metadata-preview>
<div id="tm-main" class="uk-section uk-padding-remove-top" *ngIf="showOptions.show != 'claim'">
<div>
<schema2jsonld *ngIf="url" [URL]="url" name="Link your {{openaireEntities.RESULTS}}" type="other"></schema2jsonld>
<div class="uk-container uk-container-large">
<div *ngIf="properties" class="uk-section uk-padding-remove-top">
<div *ngIf="properties && claimsProperties" class="uk-section uk-padding-remove-top">
<div class="uk-container uk-container-large">
<div class="uk-grid basketContainer" *ngIf="showOptions.show!='claim'" uk-grid>
<div class="uk-width-2-3@m uk-position-z-index uk-flex-first@m uk-flex-last uk-margin-medium-top">
<h2 *ngIf="inlineEntity" class="uk-text-center">Link source to</h2>
<h2 *ngIf="inlineEntity && claimsProperties.INLINE_ENTITY.show" class="uk-text-center">Link source to</h2>
<div *ngIf="inlineEntity && claimsProperties.INLINE_ENTITY.guideText" class="uk-text-center">{{claimsProperties.INLINE_ENTITY.guideText}}</div>
<div *ngIf="showOptions.show=='source'">
<claim-result-search-form [selectedResults]="sources" [properties]=properties
[localStoragePrefix]="localStoragePrefix+'sources'"
@ -115,16 +116,16 @@
<div class="uk-card-body uk-padding-small">
<div>
<ng-container *ngIf="inlineEntity">
<div class="uk-margin-small-bottom">
<div class="uk-text-emphasis uk-text-bolder">Source</div>
<div *ngIf="claimsProperties.INLINE_ENTITY.show" class="uk-margin-small-bottom">
<div class="uk-text-emphasis uk-text-bolder">{{claimsProperties.BASKET.source_title}}</div>
<claim-selected-results [results]="[inlineEntity]"
[localStoragePrefix]="localStoragePrefix+'sources'" class=""
[enableRemove]="false" type="source">
</claim-selected-results>
</div>
<div class="uk-padding-small uk-padding-remove-horizontal"><hr class="uk-margin-remove"/></div>
<div *ngIf="claimsProperties.INLINE_ENTITY.show" class="uk-padding-small uk-padding-remove-horizontal"><hr class="uk-margin-remove"/></div>
<div class="uk-margin-small-top">
<div class="uk-text-emphasis uk-text-bolder">Link source to <span *ngIf="results.length> 0">({{(results.length) | number}})</span></div>
<div class="uk-text-emphasis uk-text-bolder">{{claimsProperties.BASKET.target_title}} <span *ngIf="results.length> 0">({{(results.length) | number}})</span></div>
<ng-container *ngTemplateOutlet="destinations_basket"></ng-container>
</div>
</ng-container>

View File

@ -18,6 +18,7 @@ import {RouterHelper} from "../../utils/routerHelper.class";
import { Location } from '@angular/common';
import {LoginErrorCodes} from "../../login/utils/guardHelper.class";
import {UserManagementService} from "../../services/user-management.service";
import {ClaimsProperties} from "../claim-utils/claims.properties";
@Component({
selector: 'linking-generic',
@ -49,7 +50,7 @@ export class LinkingGenericComponent {
public pageContents = null;
@Input() breadcrumbs: Breadcrumb[] = [];
public routerHelper: RouterHelper = new RouterHelper();
@Input() claimsProperties:ClaimsProperties = new ClaimsProperties();
constructor (private _router: Router, private route: ActivatedRoute, private entitySearch:EntitiesSearchService,
private _meta: Meta, private _title: Title, private _piwikService:PiwikService,
private seoService: SEOService, private helper: HelperService, private cdr: ChangeDetectorRef,
@ -58,6 +59,9 @@ export class LinkingGenericComponent {
subscriptions = [];
ngOnInit() {
/* if(!this.claimsProperties){
this.claimsProperties = new ClaimsProperties();
}*/
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
if (!user) {
this.saveStateAndRedirectLogin();

View File

@ -16,10 +16,10 @@ import {ClaimEntity} from '../../claim-utils/claimHelper.class';
</div>
<div class="uk-text-small uk-flex uk-flex-wrap" [style.grid-gap]="shortVersion?'10px':'20px'"
[class.uk-margin-small-top]="!shortVersion">
<div *ngIf="entity.project.funderName">
<span class="uk-text-meta">Funder: </span>{{entity.project.funderName}}
<div *ngIf="entity.project.funderName || entity.project.funderShortname">
<span class="uk-text-meta">Funder: </span>{{entity.project.funderName?entity.project.funderName:entity.project.funderShortname}}
</div>
<div *ngIf="entity.project.code">
<div *ngIf="entity.project.code && entity.project.code!='unidentified'">
<span class="uk-text-meta">Project Code: </span>{{entity.project.code}}
</div>
</div>

View File

@ -12,6 +12,9 @@ import {ClaimEntity} from '../../claim-utils/claimHelper.class';
<span *ngIf="entity.project" class="material-icons uk-text-small uk-text-meta">
assignment_turned_in
</span>
<span *ngIf="entity.organization" class="material-icons uk-text-small uk-text-meta">
account_balance
</span>
<span *ngIf="entity.type=='community'" class="material-icons uk-text-small uk-text-meta" style="margin-right: 2px;">
people
</span>
@ -46,6 +49,9 @@ import {ClaimEntity} from '../../claim-utils/claimHelper.class';
</span>
</span>
</span>
<span *ngIf="entity.type=='organization' && entity.organization">
{{sliceString(entity.title)}}
</span>
<span *ngIf="entity.type=='community' && entity.context">
<span *ngIf=" entity.context.community != entity.context.concept.label">
{{entity.context.community }} > {{entity.context.category}} >

View File

@ -6,15 +6,14 @@
<div class="uk-width-expand">
<div class="uk-card uk-card-default">
<div class="uk-card-body">
<div class="uk-grid uk-grid-divider uk-child-width-1-2@m uk-child-width-1-1@s" uk-grid>
<div class="uk-grid uk-grid-divider uk-child-width-1-1@s" uk-grid [class.uk-child-width-1-2@m] = "claimsProperties.INLINE_ENTITY.show" >
<!-- Sources-->
<div>
<div *ngIf="claimsProperties.INLINE_ENTITY.show">
<div class=" uk-margin uk-animation-toggle">
<span class="uk-h6 "> SOURCES ({{sources.length + (inlineEntity ? 1 : 0) | number}})
<span class="uk-h6 "> {{claimsProperties.METADATA_PREVIEW.source_title}} ({{sources.length + (inlineEntity ? 1 : 0) | number}})
</span>
<a *ngIf="!inlineEntity" class="uk-float-right uk-text-small" (click)="showOptions.showSource() "> <span
uk-icon="pencil" class="uk-margin-xsmall-right"></span>Edit
sources</a>
uk-icon="pencil" class="uk-margin-xsmall-right"></span>{{claimsProperties.METADATA_PREVIEW.edit_source_title}}</a>
</div>
<ul *ngIf="inlineEntity" class="uk-list uk-animation-fade uk-list-divider">
<li>
@ -153,13 +152,15 @@
</div>
<!-- Results-->
<div class=" ">
<div class=" uk-margin ">
<span class=" uk-h6 "> LINK TO ({{results.length | number}})</span>
<div class=" uk-margin uk-grid">
<span class=" uk-h6 uk-width-expand"> {{claimsProperties.METADATA_PREVIEW.target_title}} ({{results.length | number}})</span>
<div class="uk-width-auto uk-flex">
<a class="uk-float-right uk-text-small" (click)="showOptions.showLinkTo()"> <span
uk-icon="pencil" class="uk-margin-xsmall-right"></span>Edit
entities</a>
<icon [name]="claimsProperties.METADATA_PREVIEW.edit_target_icon" [flex]="true"></icon>
<a class=" uk-margin-left-xsmall uk-text-small" (click)="showOptions.showLinkTo()">
{{claimsProperties.METADATA_PREVIEW.edit_target_title}}</a>
</div>
</div>
<div *ngIf="results.length == 0">
<div class="uk-alert dangerBorder no-selected-message uk-text-center">

View File

@ -4,6 +4,7 @@ import {ClaimEntity, ClaimsErrorMessage, Message, ShowOptions} from '../../claim
import {EnvProperties} from "../../../utils/properties/env-properties";
import {Dates} from "../../../utils/string-utils.class";
import {HelperFunctions} from "../../../utils/HelperFunctions.class";
import {ClaimsProperties} from "../../claim-utils/claims.properties";
@Component({
selector: 'metadata-preview',
@ -28,6 +29,7 @@ export class MetadataPreviewComponent {
@ViewChild(AlertModal) alertApplyAll;
@Input() localStoragePrefix: string = "";
@Input() communityId:string= null;
@Input() claimsProperties:ClaimsProperties;
errors:ClaimsErrorMessage[] = [];
warnings:Message[] = [];
public commonAccessRights = "OPEN"; // for access rights- changes when user apply a change to every entity

View File

@ -11,6 +11,7 @@ import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import {HelperModule} from "../../../utils/helper/helper.module";
import {SelectedPublicationsModule} from "./selectedResults.module";
import {IconsModule} from "../../../utils/icons/icons.module";
@NgModule({
imports: [
@ -19,7 +20,7 @@ import {SelectedPublicationsModule} from "./selectedResults.module";
ClaimEntitiesMetadataModule,
InsertClaimsModule,
MatDatepickerModule, MatNativeDateModule, MatFormFieldModule, MatInputModule, MatSelectModule,
HelperModule, SelectedPublicationsModule
HelperModule, SelectedPublicationsModule, IconsModule
],
declarations: [MetadataPreviewComponent],
exports:[MetadataPreviewComponent]

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;
@ -43,7 +44,9 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
@Input()
public id: string;
@Input()
public type: string;
set type(type: string) {
this._type = Role.mapType(type);
}
@Input()
public name: string;
@Input()
@ -53,6 +56,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 +91,13 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
@ViewChild('deleteModal') deleteModal: AlertModal;
@ViewChild('deletePendingModal') deletePendingModal: AlertModal;
@ViewChild('createRoleModal') createRoleModal: AlertModal;
public stakeholderUtils: StakeholderUtils = new StakeholderUtils();
private _type: string;
constructor(private userRegistryService: UserRegistryService,
private userManagementService: UserManagementService,
private clearCacheService: ClearCacheService,
private notificationService: NotificationService,
private router: Router,
private cdr: ChangeDetectorRef,
private fb: UntypedFormBuilder) {
}
@ -140,7 +146,7 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
updateLists() {
this.loadActive = true;
this.loadPending = true;
this.subs.push(this.userRegistryService.getActive(this.type, this.id, this.role, true).subscribe(users => {
this.subs.push(this.userRegistryService.getActive(this._type, this.id, this.role, true).subscribe(users => {
this.active = users;
this.filterActiveBySearch(this.filterForm.value.active);
this.loadActive = false;
@ -153,7 +159,7 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
}
this.loadActive = false;
}));
this.subs.push(this.userRegistryService.getPending(this.type, this.id, this.role, true).subscribe(users => {
this.subs.push(this.userRegistryService.getPending(this._type, this.id, this.role, true).subscribe(users => {
this.pending = users;
this.filterPendingBySearch(this.filterForm.value.pending);
this.loadPending = false;
@ -182,7 +188,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 +198,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);
@ -205,8 +211,8 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
this.createRoleModal.okButtonLeft = false;
this.createRoleModal.okButtonText = 'Create';
this.roleFb = this.fb.group({
name: this.fb.control(Role.roleName(this.type, this.id), Validators.required),
description: this.fb.control(Role.roleName(this.type, this.id), Validators.required)
name: this.fb.control(Role.roleName(this._type, this.id), Validators.required),
description: this.fb.control(Role.roleName(this._type, this.id), Validators.required)
});
setTimeout(() => {
this.roleFb.get('name').disable();
@ -217,7 +223,7 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
deleteActive() {
this.loadActive = true;
this.subs.push(this.userRegistryService.remove(this.type, this.id, this.selectedUser, this.role).subscribe(() => {
this.subs.push(this.userRegistryService.remove(this._type, this.id, this.selectedUser, this.role).subscribe(() => {
this.active = this.active.filter(user => user.email != this.selectedUser);
if (this.currentActivePage.length === 0) {
this.activePage = 1;
@ -235,13 +241,13 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
deletePending() {
this.loadPending = true;
this.subs.push(this.userRegistryService.cancelInvitation(this.type, this.id, this.selectedUser, this.role).subscribe(() => {
this.subs.push(this.userRegistryService.cancelInvitation(this._type, this.id, this.selectedUser, this.role).subscribe(() => {
this.pending = this.pending.filter(user => user != this.selectedUser);
this.filterPendingBySearch(this.filterForm.value.pending);
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');
@ -262,12 +268,12 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
let current = null;
let invitations = (<Array<any>>this.emailsForm.value).map(email => {
current = email;
return this.userRegistryService.invite(this.type, this.id, {
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 => {
@ -289,7 +295,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);
}
});
@ -304,12 +311,12 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
createGroup() {
this.loadActive = true;
this.loadPending = true;
this.userRegistryService.createRole(this.type, this.id).subscribe(() => {
this.userRegistryService.createRole(this._type, this.id).subscribe(() => {
NotificationHandler.rise('Group has been <b> successfully created</b>');
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,16 +330,20 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
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;
}
public get isManager(): boolean {
return this.isCurator || !!Session.isManager(this.type, this.id, this.user);
return this.isCurator || !!Session.isManager(this._type, this.id, this.user);
}
public get isCurator(): boolean {
return this.isPortalAdmin || !!Session.isCurator(this.type, this.user);
return this.isPortalAdmin || !!Session.isCurator(this._type, this.user);
}
public get isPortalAdmin(): boolean {

View File

@ -32,7 +32,7 @@
</div>
</div>
<div class="uk-width-2-5@m uk-width-1-1@s uk-text-center" uk-scrollspy-class>
<img src="assets/common-assets/fos/fos-hero-img.svg" loading="lazy">
<img src="assets/common-assets/fos/fos-hero-img.svg" loading="lazy" alt="FoS logo">
</div>
</div>
</div>

View File

@ -89,7 +89,7 @@ import {RouterHelper} from "../../utils/routerHelper.class";
</ng-container>
</span>
<ng-container *ngIf="status">
<span>{{status}}</span>
<span>{{status}} <ng-container *ngIf="currentDate <= endDate && currentDate >= startDate">(M{{calcCurrentMonth}})</ng-container></span>
</ng-container>
<ng-container *ngIf="date">
<span>{{date | date: 'dd MMM yyyy': 'UTC'}}</span>
@ -295,7 +295,7 @@ export class EntityMetadataComponent {
return this.projects.map(project => {
let value = project.funderShortname ? project.funderShortname : project.funderName;
if (project.acronym || project.title) {
value = value + ' | ' + (project.acronym ? project.acronym :
value = (value ? value + ' | ' : '') + (project.acronym ? project.acronym :
(project.title.length > 25 ? (project.title.slice(0, 25) + '...'): project.title));
}
// if(project.code) {
@ -364,4 +364,18 @@ export class EntityMetadataComponent {
this.projectsModal.open();
}
}
public get calcCurrentMonth() {
let currentDate = new Date(this.currentDate);
let startDate = new Date(this.startDate);
var months;
months = (currentDate.getFullYear() - startDate.getFullYear()) * 12;
months -= startDate.getMonth();
months += currentDate.getMonth();
if(startDate.getDate() > currentDate.getDate()) {
months--;
}
return months <= 0 ? 0 : months+1;
}
}

View File

@ -5,7 +5,6 @@ import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import {TabPagingComponent} from './tabPaging.component';
import {ShowTitleComponent} from './showTitle.component';
import {AddThisComponent} from './addThis.component';
@ -14,11 +13,11 @@ import {AddThisComponent} from './addThis.component';
CommonModule, FormsModule, RouterModule
],
declarations: [
TabPagingComponent, ShowTitleComponent, AddThisComponent
ShowTitleComponent, AddThisComponent
],
providers:[],
exports: [
TabPagingComponent, ShowTitleComponent, AddThisComponent
ShowTitleComponent, AddThisComponent
]
})
export class LandingModule { }

View File

@ -639,7 +639,11 @@ export class ParsingFunctions {
if (!classifiedSubjects.has(subject.classname)) {
classifiedSubjects.set(subject.classname, new Array<string>());
}
if(properties.environment == "production") {
classifiedSubjects.get(subject.classname).push(content);
} else {
classifiedSubjects.get(subject.classname).push(subject.classid + ": " + content);
}
// }
}
} else {
@ -659,6 +663,14 @@ export class ParsingFunctions {
}
}
}
if(properties.environment != "production" && classifiedSubjects != null) {
for (let classified of classifiedSubjects.keys()) {
subjects = subjects.concat(classifiedSubjects.get(classified));
}
classifiedSubjects = null;
}
return [subjects, otherSubjects, classifiedSubjects, fos, sdg];
}

View File

@ -1,24 +0,0 @@
import {Component, Input, Output, EventEmitter} from '@angular/core';
@Component({
selector: 'tabPaging',
template: `
<div class="uk-panel" *ngIf="!showAll && length > 10">
<a (click)="changeShowAll.emit({value: true});">
<div class="uk-float-right">view all {{length | number}}</div>
</a>
</div>
`
})
export class TabPagingComponent {
@Input() showAll: boolean;
@Input() length: number;
@Output() changeShowAll: EventEmitter<any> = new EventEmitter();
constructor () {
}
ngOnInit() {
}
}

View File

@ -18,7 +18,7 @@ import {ResultPreviewModule} from "../../../utils/result-preview/result-preview.
@NgModule({
imports: [
CommonModule, FormsModule, ResultLandingUtilsModule,
PagingModule, ErrorMessagesModule, ShowAuthorsModule, LandingModule, NoLoadPaging, ResultPreviewModule
PagingModule, ErrorMessagesModule, ShowAuthorsModule, NoLoadPaging, ResultPreviewModule
],
declarations: [
OrganizationsDeletedByInferenceComponent

View File

@ -28,7 +28,7 @@ import {HelperFunctions} from "../../../utils/HelperFunctions.class";
<li *ngFor="let result of results.slice((page-1)*pageSize, page*pageSize)">
<result-preview [modal]="modal" [properties]="properties" [hasLink]="false" [result]="getResultPreview(result)"
[showOrcid]="false" [prevPath]="prevPath" [showInline]="true"
[isDeletedByInferenceModal]="true"></result-preview>
[isDeletedByInferenceModal]="true" [isMobile]="isMobile"></result-preview>
</li>
</ul>
<no-load-paging *ngIf="results.length > pageSize" [type]="type"
@ -40,6 +40,7 @@ import {HelperFunctions} from "../../../utils/HelperFunctions.class";
`
})
export class DeletedByInferenceComponent {
@Input() isMobile: boolean = false;
@Input() prevPath: string = "";
public results: ResultLandingInfo[] = [];
@Input() id: string;

View File

@ -145,8 +145,7 @@ export class DeletedByInferenceService {
if(author.orcid_pending) {
author.orcid_pending = author.orcid_pending.toUpperCase();
}
if(result['authors'][author.rank] && results['authors'][author.rank].fullName == author.content) {
if(result['authors'][author.rank] && result['authors'][author.rank].fullName == author.content) {
if(!author.orcid && result['authors'][author.rank].orcid) {
author.orcid = result['authors'][author.rank].orcid;
} else if(!author.orcid_pending && result['authors'][author.rank].orcid_pending) {

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"
@ -1167,19 +1169,27 @@
<deletedByInference *ngIf="type == 'publication' && deleteByInferenceOpened"
[id]="resultLandingInfo.record['result']['header']['dri:objIdentifier']"
[ids]="resultLandingInfo.deletedByInferenceIds"
[resultType]="type" [type]="openaireEntities.PUBLICATIONS"></deletedByInference>
[resultType]="type" [type]="openaireEntities.PUBLICATIONS"
[isMobile]="isMobile"
[modal]="alertModalDeletedByInferenceFS"></deletedByInference>
<deletedByInference *ngIf="type == 'dataset' && deleteByInferenceOpened"
[id]="resultLandingInfo.record['result']['header']['dri:objIdentifier']"
[ids]="resultLandingInfo.deletedByInferenceIds"
[resultType]="'dataset'" [type]="openaireEntities.DATASETS"></deletedByInference>
[resultType]="'dataset'" [type]="openaireEntities.DATASETS"
[isMobile]="isMobile"
[modal]="alertModalDeletedByInferenceFS"></deletedByInference>
<deletedByInference *ngIf="type == 'software' && deleteByInferenceOpened"
[id]="resultLandingInfo.record['result']['header']['dri:objIdentifier']"
[ids]="resultLandingInfo.deletedByInferenceIds"
[resultType]="type" [type]="openaireEntities.SOFTWARE"></deletedByInference>
[resultType]="type" [type]="openaireEntities.SOFTWARE"
[isMobile]="isMobile"
[modal]="alertModalDeletedByInferenceFS"></deletedByInference>
<deletedByInference *ngIf="type == 'orp' && deleteByInferenceOpened"
[id]="resultLandingInfo.record['result']['header']['dri:objIdentifier']"
[ids]="resultLandingInfo.deletedByInferenceIds"
[resultType]="'other'" [type]="openaireEntities.OTHER"></deletedByInference>
[resultType]="'other'" [type]="openaireEntities.OTHER"
[isMobile]="isMobile"
[modal]="alertModalDeletedByInferenceFS"></deletedByInference>
</fs-modal>
<fs-modal *ngIf="resultLandingInfo" #citeFsModal classTitle="uk-tile-default uk-border-bottom">

View File

@ -38,6 +38,7 @@ 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";
import {SearchTabModule} from "../../utils/tabs/contents/search-tab.module";
@NgModule({
imports: [

View File

@ -20,7 +20,7 @@ import {StakeholderBaseComponent} from "../../utils/stakeholder-base.component";
template: `
<div class="uk-margin-medium-bottom">
<form *ngIf="stakeholderFb" [formGroup]="stakeholderFb">
<div class="uk-grid uk-grid-large" uk-grid>
<div class="uk-grid" uk-grid>
<div class="uk-width-1-2@m">
<div input id="name" [formInput]="stakeholderFb.get('name')"
placeholder="Name"></div>
@ -126,6 +126,19 @@ import {StakeholderBaseComponent} from "../../utils/stakeholder-base.component";
</div>
</div>
</div>
<div *ngIf="canChangeCopy" class="uk-width-1-1">
<h6>Instance Type</h6>
<div class="uk-width-auto uk-flex uk-flex-middle">
<label class="uk-margin-right">
<input type="radio" [value]="true" formControlName="copy"/>
<span class="uk-margin-xsmall-left">Copy</span>
</label>
<label class="uk-margin-right">
<input type="radio" [value]="false" formControlName="copy"/>
<span class="uk-margin-xsmall-left">Reference</span>
</label>
</div>
</div>
</div>
</form>
<div #notify [class.uk-hidden]="!stakeholderFb" notify-form
@ -145,6 +158,7 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
public stakeholder: Stakeholder;
public isDefault: boolean;
public isNew: boolean;
public isFull: boolean;
public loading: boolean = false;
public typesByRole: Option[];
public statsProfiles: string[];
@ -172,7 +186,7 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
super.ngOnDestroy();
}
public init(stakeholder: Stakeholder, alias: string[], defaultStakeholders: Stakeholder[], isDefault: boolean, isNew: boolean) {
public init(stakeholder: Stakeholder, alias: string[], defaultStakeholders: Stakeholder[], isDefault: boolean, isNew: boolean, isFull: boolean = false) {
this.reset();
this.deleteCurrentPhoto = false;
this.stakeholder = stakeholder;
@ -183,6 +197,7 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
this.defaultStakeholders = defaultStakeholders;
this.isDefault = isDefault;
this.isNew = isNew;
this.isFull = isFull;
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
this.user = user;
if (this.isCurator) {
@ -220,6 +235,7 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
funderType: this.fb.control(this.stakeholder.funderType),
topics: this.fb.control(this.stakeholder.topics),
isUpload: this.fb.control(this.stakeholder.isUpload),
copy: this.fb.control(this.stakeholder.copy),
logoUrl: this.fb.control(this.stakeholder.logoUrl),
});
if (this.stakeholder.isUpload) {
@ -246,7 +262,7 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
this.subscriptions.push(this.stakeholderFb.get('type').valueChanges.subscribe(value => {
this.onTypeChange(value, defaultStakeholders);
}));
this.stakeholderFb.setControl('defaultId', this.fb.control(stakeholder.defaultId, (this.isDefault && !this.isNew) ? [] : Validators.required));
this.stakeholderFb.setControl('defaultId', this.fb.control(this.stakeholder.defaultId, (this.isDefault && !this.isNew) ? [] : Validators.required));
if (!this.isNew) {
this.notification = NotificationUtils.editStakeholder(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
this.notify.reset(this.notification.message);
@ -304,6 +320,13 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
return this.isNew && this.stakeholderFb.get('type').valid && !!this.defaultStakeholdersOptions;
}
public get canChangeCopy(): boolean {
return this.isCurator &&
!this.stakeholderFb.get('isDefault').getRawValue() &&
this.stakeholderFb.get('defaultId').getRawValue() &&
this.stakeholderFb.get('defaultId').getRawValue() !== '-1';
}
reset() {
this.uploadError = null;
this.stakeholderFb = null;
@ -350,15 +373,15 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
public saveStakeholder(callback: Function, errorCallback: Function = null) {
if (this.isNew) {
let defaultStakeholder = this.defaultStakeholders.find(value => value._id === this.stakeholderFb.getRawValue().defaultId);
this.stakeholderFb.setValue(this.stakeholderUtils.createFunderFromDefaultProfile(this.stakeholderFb.getRawValue(),
(defaultStakeholder ? defaultStakeholder.topics : []), this.stakeholderFb.getRawValue().isDefault));
this.removePhoto();
let copyId = null;
if (this.stakeholderFb.getRawValue().isDefault) {
copyId = this.stakeholderFb.getRawValue().defaultId !== '-1'?this.stakeholderFb.getRawValue().defaultId:null;
this.stakeholderFb.get('defaultId').setValue(null);
this.stakeholderFb.removeControl('isDefault');
}
this.removePhoto();
this.subscriptions.push(this.stakeholderService.buildStakeholder(this.properties.monitorServiceAPIURL,
this.stakeholderFb.getRawValue()).subscribe(stakeholder => {
this.stakeholderFb.getRawValue(), copyId).subscribe(stakeholder => {
this.notification.entity = stakeholder._id;
this.notification.stakeholder = stakeholder.alias;
this.notification.stakeholderType = stakeholder.type;
@ -375,7 +398,7 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
this.loading = false;
}));
} else {
this.subscriptions.push(this.stakeholderService.saveElement(this.properties.monitorServiceAPIURL, this.stakeholderFb.getRawValue()).subscribe(stakeholder => {
this.subscriptions.push(this.stakeholderService.saveElement(this.properties.monitorServiceAPIURL, this.stakeholderFb.getRawValue(), [], this.isFull).subscribe(stakeholder => {
this.notification.entity = stakeholder._id;
this.notification.stakeholder = stakeholder.alias;
this.notification.stakeholderType = stakeholder.type;

View File

@ -5,9 +5,10 @@ import {InputModule} from "../../../sharedComponents/input/input.module";
import {ReactiveFormsModule} from "@angular/forms";
import {IconsModule} from "../../../utils/icons/icons.module";
import {NotifyFormModule} from "../../../notifications/notify-form/notify-form.module";
import {MatSlideToggleModule} from "@angular/material/slide-toggle";
@NgModule({
imports: [CommonModule, InputModule, ReactiveFormsModule, IconsModule, NotifyFormModule],
imports: [CommonModule, InputModule, ReactiveFormsModule, IconsModule, NotifyFormModule, MatSlideToggleModule],
declarations: [EditStakeholderComponent],
exports: [EditStakeholderComponent]
})

View File

@ -48,7 +48,7 @@ export class GeneralComponent extends BaseComponent implements OnInit {
}
public reset() {
this.editStakeholderComponent.init(this.stakeholder, this.alias, this.defaultStakeholders, this.stakeholder.defaultId == null, false)
this.editStakeholderComponent.init(this.stakeholder, this.alias, this.defaultStakeholders, this.stakeholder.defaultId == null, false, true)
}
public save() {

View File

@ -31,7 +31,7 @@
<div *ngIf="!loading" uk-height-match="target: .titleContainer; row: false">
<div uk-height-match="target: .logoContainer; row: false">
<div *ngIf="tab != 'profiles' && isCurator()" class="uk-section">
<div class="uk-flex uk-flex-middle uk-flex-between uk-margin-small-bottom">
<div class="uk-flex uk-flex-middle uk-flex-between uk-margin-bottom">
<h4 class="uk-margin-remove">Profile Templates</h4>
<paging-no-load *ngIf="displayDefaultStakeholders?.length > pageSize"
(pageChange)="updateCurrentTemplatesPage($event)"
@ -39,8 +39,7 @@
[totalResults]="displayDefaultStakeholders.length">
</paging-no-load>
</div>
<div class="uk-grid uk-child-width-1-3@l uk-child-width-1-2@m uk-child-width-1-1 uk-grid-match"
uk-grid>
<div class="uk-grid uk-child-width-1-3@l uk-child-width-1-2@m uk-child-width-1-1 uk-grid-match" uk-grid>
<ng-template ngFor
[ngForOf]="displayDefaultStakeholders.slice((currentTemplatesPage-1)*pageSize, currentTemplatesPage*pageSize)"
let-stakeholder>
@ -59,7 +58,7 @@
</h4>
</div>
<div *ngIf="tab != 'templates' && isManager()" class="uk-section">
<div class="uk-flex uk-flex-middle uk-flex-between uk-margin-small-bottom">
<div class="uk-flex uk-flex-middle uk-flex-between uk-margin-bottom">
<h4 class="uk-margin-remove">Profiles</h4>
<paging-no-load *ngIf="displayStakeholders?.length > pageSize"
(pageChange)="updateCurrentPage($event)"

View File

@ -4,9 +4,9 @@
<div class="uk-grid uk-grid-large uk-child-width-1-1" uk-grid>
<div *ngFor="let number of numbers; let i=index">
<div class="section">
<div class="tools">
<div *ngIf="isEditable" class="tools">
<div class="uk-flex uk-flex-middle">
<a [class.uk-disabled]="editing" class="" (click)="createSection(i, 'number')"
<a *ngIf="isCurator" [class.uk-disabled]="editing" class="" (click)="createSection(i, 'number')"
uk-tooltip="Create a new section">
<icon name="add" [flex]="true"></icon>
</a>
@ -17,25 +17,33 @@
</div>
</div>
<div *ngIf="numberSections.at(i)" class="uk-margin-medium-bottom">
<div input [formInput]="numberSections.at(i).get('title')"
<div *ngIf="isEditable" input [formInput]="numberSections.at(i).get('title')"
(focusEmitter)="saveSection($event, numberSections.at(i), i, 'number')"
class="uk-width-1-3@m uk-width-1-1" placeholder="Title" inputClass="border-bottom"></div>
<h5 *ngIf="!isEditable" class="uk-margin-remove">{{numberSections.at(i).get('title').value}}</h5>
</div>
<div [id]="'number-' + number._id" class="uk-grid uk-grid-small uk-grid-match" uk-sortable="group: number" uk-grid>
<div [id]="'number-' + number._id" class="uk-grid uk-grid-small uk-grid-match"
[attr.uk-sortable]="isEditable ? 'group: number': null" uk-grid>
<ng-template ngFor [ngForOf]="number.indicators" let-indicator let-j="index">
<div *ngIf="indicator" [id]="indicator._id"
[ngClass]="getNumberClassBySize(indicator.width)">
<div class="uk-card uk-card-default uk-padding-small number-card uk-position-relative">
<div *ngIf="!dragging"
class="uk-position-top-right uk-margin-small-right uk-margin-small-top">
<a class="uk-link-reset uk-flex uk-flex-middle" [class.uk-disabled]="editing">
<icon *ngIf="!isEditable && showVisibility" [flex]="true"
[name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)"
ratio="0.6"></icon>
<a *ngIf="isEditable && (isCurator || showVisibility)" class="uk-link-reset uk-flex uk-flex-middle" [class.uk-disabled]="editing">
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)" ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element class="uk-dropdown" uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0">
<div #element *ngIf="isEditable && (isCurator || showVisibility)" class="uk-dropdown"
uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0">
<ul class="uk-nav uk-dropdown-nav">
<ng-container *ngIf="isCurator">
<li><a (click)="editNumberIndicatorOpen(number, indicator._id); hide(element)">Edit</a></li>
<li>
<a (click)="editNumberIndicatorOpen(number, indicator._id); hide(element)">Edit</a>
</li>
</ng-container>
<ng-container *ngIf="showVisibility">
<li *ngIf="isCurator" class="uk-nav-divider"></li>
@ -43,9 +51,12 @@
<li>
<a (click)="changeIndicatorStatus(number._id, indicator, v.value);hide(element)">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" [name]="v.icon" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">{{v.label}}</span>
<icon *ngIf="indicator.visibility === v.value" [flex]="true" name="done" class="uk-text-secondary" ratio="0.8"></icon>
<icon [flex]="true" [name]="v.icon"
ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">{{ v.label }}</span>
<icon *ngIf="indicator.visibility === v.value"
[flex]="true" name="done"
class="uk-text-secondary" ratio="0.8"></icon>
</div>
</a>
</li>
@ -53,24 +64,28 @@
</ng-container>
<ng-container *ngIf="!indicator.defaultId && !editing && isCurator">
<li class="uk-nav-divider">
<li><a (click)="deleteIndicatorOpen(number, indicator._id, 'number', 'delete');hide(element)">Delete</a></li>
<li>
<a (click)="deleteIndicatorOpen(number, indicator._id, 'number', 'delete');hide(element)">Delete</a>
</li>
</ng-container>
</ul>
</div>
</div>
<div class="uk-text-small uk-text-truncate uk-margin-xsmall-bottom uk-margin-right">{{indicator.name}}</div>
<div class="uk-text-small uk-text-truncate uk-margin-xsmall-bottom uk-margin-right">{{ indicator.name }}</div>
<div class="number uk-text-small uk-text-bold">
<span *ngIf="numberResults.get(i + '-' + j)" [innerHTML]="(indicator.indicatorPaths[0].format == 'NUMBER'?(numberResults.get(i + '-' + j) | numberRound: 2:1:stakeholder.locale):(numberResults.get(i + '-' + j) | numberPercentage: stakeholder.locale))"></span>
<span *ngIf="!numberResults.get(i + '-' + j)">--</span>
<span *ngIf="numberResults.get(i + '-' + j + '-' + 0)"
[innerHTML]="(indicator.indicatorPaths[0].format == 'NUMBER'?(numberResults.get(i + '-' + j + '-' + 0) | numberRound: 2:1:stakeholder.locale):(numberResults.get(i + '-' + j + '-' + 0) | numberPercentage: stakeholder.locale))"></span>
<span *ngIf="!numberResults.get(i + '-' + j + '-' + 0)">--</span>
</div>
</div>
</div>
</ng-template>
</div>
<div *ngIf="isCurator" class="uk-margin-top">
<div *ngIf="isCurator && isEditable" class="uk-margin-top">
<div class="uk-grid uk-grid-small" uk-grid>
<div [ngClass]="getNumberClassBySize('small')">
<a class="uk-card uk-card-default number-card uk-padding-small uk-flex uk-flex-middle uk-link-reset" (click)="editNumberIndicatorOpen(number)">
<a class="uk-card uk-card-default number-card uk-padding-small uk-flex uk-flex-middle uk-link-reset"
(click)="editNumberIndicatorOpen(number)">
<div class="uk-text-background uk-margin-right">
<icon name="add" [flex]="true" ratio="1.5"></icon>
</div>
@ -83,7 +98,7 @@
</div>
</div>
</div>
<div>
<div *ngIf="isEditable && isCurator">
<ng-container *ngTemplateOutlet="new_section; context:{type: 'number'}"></ng-container>
</div>
</div>
@ -93,9 +108,9 @@
<div class="uk-grid uk-grid-large uk-child-width-1-1" uk-grid>
<div *ngFor="let chart of charts; let i=index">
<div class="section uk-margin-top uk-padding-small">
<div class="tools">
<div *ngIf="isEditable" class="tools">
<div class="uk-flex uk-flex-middle">
<a [class.uk-disabled]="editing" class="" (click)="createSection(i)"
<a *ngIf="isCurator" [class.uk-disabled]="editing" class="" (click)="createSection(i)"
title="Create a new section">
<icon name="add" [flex]="true"></icon>
</a>
@ -107,24 +122,33 @@
</div>
<div *ngIf="chartSections.at(i)"
class="uk-margin-medium-bottom">
<div input [formInput]="chartSections.at(i).get('title')"
<div *ngIf="isEditable" input [formInput]="chartSections.at(i).get('title')"
(focusEmitter)="saveSection($event, chartSections.at(i), i)"
class="uk-width-1-3@m uk-width-1-1" placeholder="Title" inputClass="border-bottom"></div>
<h5 *ngIf="!isEditable" class="uk-margin-remove">{{chartSections.at(i).get('title').value}}</h5>
</div>
<div [id]="'chart-' + chart._id" class="uk-grid uk-grid-small uk-grid-match" uk-sortable="group: chart" uk-grid>
<div [id]="'chart-' + chart._id" class="uk-grid uk-grid-small uk-grid-match"
[attr.uk-sortable]="isEditable ? 'group: chart': null" uk-grid>
<ng-template ngFor [ngForOf]="chart.indicators" let-indicator let-j="index">
<div *ngIf="indicator" [id]="indicator._id" [ngClass]="getChartClassBySize(indicator.width)">
<div *ngIf="indicator" [id]="indicator._id"
[ngClass]="getChartClassBySize(indicator.width)">
<div class="uk-card uk-card-default uk-card-body uk-position-relative">
<div *ngIf="!dragging"
class="uk-position-top-right uk-margin-small-right uk-margin-small-top">
<a class="uk-link-reset uk-flex uk-flex-middle" [class.uk-disabled]="editing">
<icon *ngIf="!isEditable && showVisibility" [flex]="true"
[name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)"
ratio="0.6"></icon>
<a *ngIf="isEditable && (isCurator || showVisibility)" class="uk-link-reset uk-flex uk-flex-middle" [class.uk-disabled]="editing">
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)" ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element class="uk-dropdown" uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0">
<div #element *ngIf="isEditable && (isCurator || showVisibility)" class="uk-dropdown"
uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0">
<ul class="uk-nav uk-dropdown-nav">
<ng-container *ngIf="isCurator">
<li><a (click)="editChartIndicatorOpen(chart, indicator._id); hide(element)">Edit</a></li>
<li>
<a (click)="editChartIndicatorOpen(chart, indicator._id); hide(element)">Edit</a>
</li>
</ng-container>
<ng-container *ngIf="showVisibility">
<li *ngIf="isCurator" class="uk-nav-divider"></li>
@ -132,9 +156,12 @@
<li>
<a (click)="changeIndicatorStatus(chart._id, indicator, v.value);">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" [name]="v.icon" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">{{v.label}}</span>
<icon *ngIf="indicator.visibility === v.value" [flex]="true" name="done" class="uk-text-secondary" ratio="0.8"></icon>
<icon [flex]="true" [name]="v.icon"
ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">{{ v.label }}</span>
<icon *ngIf="indicator.visibility === v.value"
[flex]="true" name="done"
class="uk-text-secondary" ratio="0.8"></icon>
</div>
</a>
</li>
@ -142,45 +169,66 @@
</ng-container>
<ng-container *ngIf="!indicator.defaultId && !editing && isCurator">
<li class="uk-nav-divider">
<li><a (click)="deleteIndicatorOpen(chart, indicator._id, 'chart', 'delete');hide(element)">Delete</a></li>
<li>
<a (click)="deleteIndicatorOpen(chart, indicator._id, 'chart', 'delete');hide(element)">Delete</a>
</li>
</ng-container>
</ul>
</div>
</div>
<div>
<div *ngIf="indicator.name" class="uk-text-center uk-text-bold uk-margin-small-bottom">
{{indicator.name}}
<div *ngIf="indicator.name"
class="uk-text-center uk-text-bold uk-margin-small-bottom">
{{ indicator.name }}
</div>
<iframe *ngIf="!properties.disableFrameLoad && indicator.indicatorPaths[0] && indicator.indicatorPaths[0].source !=='image' &&
safeUrls.get(indicatorUtils.getFullUrl(stakeholder, indicator.indicatorPaths[0]))"
allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"
[src]="safeUrls.get(indicatorUtils.getFullUrl(stakeholder, indicator.indicatorPaths[0]))"
class="uk-width-1-1" [ngClass]="'uk-height-' + (indicator.height?indicator.height.toLowerCase():'medium')"
<div *ngIf="indicator.indicatorPaths.length > 1" class="uk-margin-medium-bottom">
<ul class="uk-subnav uk-subnav-pill uk-subnav-small">
<li *ngFor="let indicatorPath of indicator.indicatorPaths; let i=index"
class="uk-flex uk-margin-small-top"
[class.uk-active]="(!indicator.activePath && i == 0) || indicator.activePath === i">
<a (click)="indicator.activePath = i">
<span>
{{ indicatorPath.parameters.title ? indicatorPath.parameters.title : '--' }}
</span>
</a>
</li>
</ul>
</div>
<iframe *ngIf="!properties.disableFrameLoad && getActiveIndicatorPath(indicator) && getActiveIndicatorPath(indicator).source !=='image' &&
safeUrls.get(indicatorUtils.getFullUrl(stakeholder, getActiveIndicatorPath(indicator)))"
allowfullscreen="true" mozallowfullscreen="true"
webkitallowfullscreen="true"
[src]="safeUrls.get(indicatorUtils.getFullUrl(stakeholder, getActiveIndicatorPath(indicator)))"
class="uk-width-1-1"
[ngClass]="'uk-height-' + (indicator.height?indicator.height.toLowerCase():'medium')"
[class.uk-blend-multiply]="!isFullscreen"></iframe>
<div *ngIf="properties.disableFrameLoad && indicator.indicatorPaths &&
indicator.indicatorPaths.length > 0 && indicator.indicatorPaths[0].source !=='image'">
<img class="uk-width-1-1 uk-blend-multiply" [ngClass]="'uk-height-' + (indicator.height?indicator.height.toLowerCase():'medium')"
indicator.indicatorPaths.length > 0 && getActiveIndicatorPath(indicator).source !=='image'">
<img class="uk-width-1-1 uk-blend-multiply"
[ngClass]="'uk-height-' + (indicator.height?indicator.height.toLowerCase():'medium')"
src="assets/chart-placeholder.png">
</div>
<div *ngIf="indicator.indicatorPaths && indicator.indicatorPaths[0] &&
indicator.indicatorPaths[0].source === 'image'">
<img class="uk-width-1-1 uk-blend-multiply" [ngClass]="'uk-height-' + (indicator.height?indicator.height.toLowerCase():'medium')"
[src]="indicator.indicatorPaths[0].url">
<div *ngIf="indicator.indicatorPaths && getActiveIndicatorPath(indicator) && getActiveIndicatorPath(indicator).source === 'image'">
<img class="uk-width-1-1 uk-blend-multiply"
[ngClass]="'uk-height-' + (indicator.height?indicator.height.toLowerCase():'medium')"
[src]="getActiveIndicatorPath(indicator).url">
</div>
</div>
</div>
</div>
</ng-template>
</div>
<div *ngIf="isCurator" class="uk-margin-top">
<div *ngIf="isCurator && isEditable" class="uk-margin-top">
<div class="uk-grid uk-grid-small uk-grid-match" uk-grid>
<div [ngClass]="getChartClassBySize('small')">
<div class=" uk-card uk-card-default uk-card-body clickable" (click)="editChartIndicatorOpen(chart)">
<div class=" uk-card uk-card-default uk-card-body clickable"
(click)="editChartIndicatorOpen(chart)">
<h6 class="uk-text-bold uk-text-center">
Create a custom indicator
</h6>
<div class="uk-text-muted uk-text-small">
Use our advance tool to create a custom Indicator that suit the needs of your funding
Use our advance tool to create a custom Indicator that suit the needs of your
funding
KPI's.
</div>
<div class="uk-flex uk-flex-center uk-text-background uk-margin-medium-top">
@ -192,7 +240,7 @@
</div>
</div>
</div>
<div>
<div *ngIf="isEditable && isCurator">
<ng-container *ngTemplateOutlet="new_section; context:{type: 'chart'}"></ng-container>
</div>
</div>
@ -212,9 +260,11 @@
<div *ngIf="numberIndicatorFb" class="uk-grid" [formGroup]="numberIndicatorFb" uk-grid>
<div input class="uk-width-1-1" [formInput]="numberIndicatorFb.get('name')" placeholder="Title"></div>
<div *ngIf="stakeholder.defaultId != '-1' && ( (indicator.description && indicator.description.length > 0) || !stakeholder.defaultId)"
input class="uk-width-1-1" [formInput]="numberIndicatorFb.get('description')" placeholder="Profile description" type="textarea">
input class="uk-width-1-1" [formInput]="numberIndicatorFb.get('description')"
placeholder="Profile description" type="textarea">
</div>
<div input class="uk-width-1-1" *ngIf="stakeholder.defaultId" [formInput]="numberIndicatorFb.get('additionalDescription')"
<div input class="uk-width-1-1" *ngIf="stakeholder.defaultId"
[formInput]="numberIndicatorFb.get('additionalDescription')"
placeholder="Description" type="textarea">
</div>
<div *ngIf="showVisibility" input class="uk-width-1-2@m" [formInput]="numberIndicatorFb.get('visibility')"
@ -223,29 +273,87 @@
<div input class="uk-width-1-2@m" [formInput]="numberIndicatorFb.get('width')"
placeholder="Number Size" [options]="indicatorUtils.indicatorSizes" type="select">
</div>
<hr class="uk-width-1-1">
<div *ngIf="numberIndicatorPaths" formArrayName="indicatorPaths">
<div *ngIf="stakeholderUtils.hasMultiNumberIndicatorPaths" class="uk-margin-medium-bottom">
<ul #numbersTransition class="uk-subnav uk-subnav-pill uk-subnav-small" transition-group>
<li *ngFor="let indicatorPath of numberIndicatorPaths.controls; let i=index"
class="uk-visible-toggle uk-flex uk-margin-small-top" transition-group-item
[class.uk-active]="(!indicator.activePath && i == 0) || indicator.activePath === i">
<a (click)="activeNumberIndicatorPath(i)">
<span *ngIf="indicatorPath.get('result').valid && indicatorPath.get('result').value !== 0"
[innerHTML]="(indicatorPath.get('format').value == 'NUMBER'?(indicatorPath.get('result').value | numberRound: 2:1:stakeholder.locale):(indicatorPath.get('result').value | numberPercentage: stakeholder.locale))">
</span>
<span *ngIf="!indicatorPath.get('result').valid || indicatorPath.get('result').value === 0">
--
</span>
</a>
<span *ngIf="!indicator.defaultId && numberIndicatorPaths.length > 1"
class="uk-flex uk-flex-column uk-flex-center uk-margin-small-left"
[class.uk-invisible-hover]="indicator.activePath !== i"
(click)="$event.stopPropagation();$event.preventDefault()">
<a *ngIf="isCurator" class="uk-link-reset uk-flex uk-flex-middle">
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0;">
<ul class="uk-nav uk-dropdown-nav">
<li *ngIf="i > 0">
<a (click)="hide(element);moveIndicatorPath(numberIndicatorFb, 'number', i)">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" name="west" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">Move Left</span>
</div>
</a>
</li>
<li *ngIf="i < numberIndicatorPaths.length - 1">
<a (click)="hide(element);moveIndicatorPath(numberIndicatorFb, 'number', i, i + 1)">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" name="east" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">Move Right</span>
</div>
</a>
</li>
<li>
<a (click)="removeNumberIndicatorPath(i); hide(element)">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" name="delete" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">Delete</span>
</div>
</a>
</li>
</ul>
</div>
</span>
</li>
<li class="uk-margin-small-top">
<a (click)="activeNumberIndicatorPath(numberIndicatorPaths.length); $event.preventDefault()"
class="uk-flex uk-flex-middle">
<icon name="add" [flex]="true"></icon>
<span class="uk-text-uppercase">Add</span>
</a>
</li>
</ul>
</div>
<div *ngFor="let indicatorPath of numberIndicatorPaths.controls; let i=index" [formGroupName]="i">
<div class="uk-grid" uk-grid>
<div *ngIf="(!indicator.activePath && i == 0) || indicator.activePath === i" class="uk-grid"
uk-grid>
<div class="uk-width-1-1">
<div class="uk-grid" uk-grid>
<div class="uk-width-1-1 uk-flex uk-flex-middle">
<div input class="uk-width-expand" [formInput]="indicatorPath.get('url')" placeholder="Number URL">
<div *ngIf="urlParameterizedMessage" warning>{{urlParameterizedMessage}}</div>
<div input class="uk-width-expand" [formInput]="indicatorPath.get('url')"
placeholder="Number URL">
</div>
<div class='uk-padding-small'>
<a class="uk-link-reset" (click)="copyToClipboard(indicatorPath.get('url').value)"><icon [flex]="true" name="content_copy"></icon></a>
</div>
</div>
<div *ngIf="showCheckForSchemaEnhancements" class="uk-width-1-1">
<div class="uk-alert uk-alert-warning">
There are schema enhancements that can be applied in this query.<a
(click)="indicatorPath.get('url').setValue(indicatorUtils.applySchemaEnhancements(indicatorPath.get('url').value)); indicatorPath.get('url').markAsDirty()">Apply
now</a>
<a class="uk-link-reset"
(click)="copyToClipboard(indicatorPath.get('url').value)">
<icon [flex]="true" name="content_copy"></icon>
</a>
</div>
</div>
<div class="uk-width-1-2@m">
<div input [formInput]="indicatorPath.get('source')" placeholder="Source"
[options]="isAdministrator?indicatorUtils.allSourceTypes:indicatorUtils.sourceTypes" type="select">
[options]="isAdministrator?indicatorUtils.allSourceTypes:indicatorUtils.sourceTypes"
type="select">
</div>
</div>
<div class="uk-width-1-2@m">
@ -254,7 +362,8 @@
</div>
</div>
<div class="uk-width-1-2@m" formArrayName="parameters">
<div *ngIf="getParameter(i, 'statsProfile', 'number') && statsProfiles" input [formInput]="getParameter(i, 'statsProfile', 'number').get('value')"
<div *ngIf="getParameter(i, 'statsProfile', 'number') && statsProfiles" input
[formInput]="getParameter(i, 'statsProfile', 'number').get('value')"
[type]="'select'"
[options]="statsProfiles"
placeholder="Stats Profile"></div>
@ -265,22 +374,26 @@
<h6 class="uk-text-bold uk-margin-remove-bottom">
<span>JSON Path</span>
</h6>
<div *ngIf="numberIndicatorPaths.at(i).get('result').invalid && numberIndicatorPaths.at(i).get('result').errors.required">
<div *ngIf="indicatorPath.get('result').invalid && indicatorPath.get('result').errors.required">
<div class="uk-text-danger uk-text-small">
This JSON path is not valid or the result has not been calculated yet.
Please press calculate on box below to see the result.
</div>
</div>
<div class="uk-grid uk-child-width-1-3@m uk-child-width-1-1 uk-margin-top uk-flex-middle" uk-grid>
<div *ngFor="let jsonPath of getJsonPath(i).controls; let j=index" class="uk-flex uk-flex-middle">
<div input class="uk-width-1-1" [formInput]="jsonPath" [placeholder]="'Level ' + +(j + 1)"></div>
<div class="uk-grid uk-child-width-1-3@m uk-child-width-1-1 uk-margin-top uk-flex-middle"
uk-grid>
<div *ngFor="let jsonPath of getJsonPath(i).controls; let j=index"
class="uk-flex uk-flex-middle">
<div input class="uk-width-1-1" [formInput]="jsonPath"
[placeholder]="'Level ' + +(j + 1)"></div>
<a [class.uk-invisible]="getJsonPath(i).length === 1 || numberIndicatorFb.get('defaultId').value"
class="uk-margin-small-left uk-text-danger"
[class.uk-disabled]="getJsonPath(i).disabled"
(click)="removeJsonPath(i, j)">
<icon name="close"></icon>
</a>
<span [class.uk-invisible]="getJsonPath(i).disabled || j === (getJsonPath(i).controls.length - 1)" class="uk-text-center uk-margin-small-left">
<span [class.uk-invisible]="getJsonPath(i).disabled || j === (getJsonPath(i).controls.length - 1)"
class="uk-text-center uk-margin-small-left">
<icon name="east"></icon>
</span>
</div>
@ -294,24 +407,24 @@
<div class="uk-width-1-1 uk-flex uk-flex-center">
<div class="uk-flex uk-position-relative">
<span class="uk-padding number number-preview uk-flex uk-flex-column uk-flex-center uk-text-center">
<span *ngIf="numberIndicatorPaths.at(i).get('result').valid && numberIndicatorPaths.at(i).get('result').value !== 0"
[innerHTML]="(numberIndicatorPaths.at(i).get('format').value == 'NUMBER'?(numberIndicatorPaths.at(i).get('result').value | numberRound: 2:1:stakeholder.locale):(numberIndicatorPaths.at(i).get('result').value | numberPercentage: stakeholder.locale))">
<span *ngIf="indicatorPath.get('result').valid && indicatorPath.get('result').value !== 0"
[innerHTML]="(indicatorPath.get('format').value == 'NUMBER'?(indicatorPath.get('result').value | numberRound: 2:1:stakeholder.locale):(indicatorPath.get('result').value | numberPercentage: stakeholder.locale))">
</span>
<span *ngIf="numberIndicatorPaths.at(i).get('result').valid && numberIndicatorPaths.at(i).get('result').value === 0">
<span *ngIf="indicatorPath.get('result').valid && indicatorPath.get('result').value === 0">
--
</span>
</span>
<div *ngIf="numberIndicatorPaths.at(i).get('result').invalid"
<div *ngIf="indicatorPath.get('result').invalid"
class="uk-width-1-1 uk-height-1-1 refresh-indicator">
<div class="uk-position-relative uk-height-1-1">
<a class="uk-position-center uk-text-center uk-text-small uk-link-reset"
[class.uk-disabled]="numberIndicatorPaths.at(i).get('url').invalid"
[class.uk-disabled]="indicatorPath.get('url').invalid"
(click)="validateJsonPath(i, true)">
<div>
<icon name="refresh"></icon>
</div>
<span *ngIf="numberIndicatorPaths.at(i).get('result').errors.required">Calculate</span>
<span *ngIf="numberIndicatorPaths.at(i).get('result').errors.validating">Calculating...</span>
<span *ngIf="indicatorPath.get('result').errors.required">Calculate</span>
<span *ngIf="indicatorPath.get('result').errors.validating">Calculating...</span>
</a>
</div>
</div>
@ -319,13 +432,13 @@
class="uk-width-1-1 uk-height-1-1 refresh-indicator">
<div class="uk-position-relative uk-height-1-1">
<a class="uk-position-center uk-text-center uk-text-small uk-link-reset"
[class.uk-disabled]="numberIndicatorPaths.at(i).get('url').invalid"
[class.uk-disabled]="indicatorPath.get('url').invalid"
(click)="refreshIndicator('number')">
<div>
<icon name="refresh"></icon>
</div>
<span>Calculate</span>
<span *ngIf="numberIndicatorPaths.at(i).get('result').errors?.validating">Calculating...</span>
<span *ngIf="indicatorPath.get('result').errors?.validating">Calculating...</span>
</a>
</div>
</div>
@ -347,9 +460,11 @@
<div *ngIf="chartIndicatorFb" [formGroup]="chartIndicatorFb" class="uk-grid" uk-grid>
<div input class="uk-width-1-1" [formInput]="chartIndicatorFb.get('name')" placeholder="Title"></div>
<div *ngIf="stakeholder.defaultId != '-1' && ((indicator.description && indicator.description.length > 0) || !stakeholder.defaultId)"
input class="uk-width-1-1" [formInput]="chartIndicatorFb.get('description')" placeholder="Default Description" type="textarea">
input class="uk-width-1-1" [formInput]="chartIndicatorFb.get('description')"
placeholder="Default Description" type="textarea">
</div>
<div *ngIf="stakeholder.defaultId" input class="uk-width-1-1" [formInput]="chartIndicatorFb.get('additionalDescription')"
<div *ngIf="stakeholder.defaultId" input class="uk-width-1-1"
[formInput]="chartIndicatorFb.get('additionalDescription')"
placeholder="Description" type="textarea">
</div>
<div *ngIf="showVisibility" input class="uk-width-1-2@m" [formInput]="chartIndicatorFb.get('visibility')"
@ -362,47 +477,111 @@
[options]="indicatorUtils.indicatorSizes" type="select">
</div>
<div *ngIf="chartIndicatorPaths" formArrayName="indicatorPaths" class="uk-width-1-1">
<div *ngIf="stakeholderUtils.hasMultiChartIndicatorPaths" class="uk-margin-medium-bottom">
<ul #chartsTransition class="uk-subnav uk-subnav-pill uk-subnav-small" transition-group>
<li *ngFor="let indicatorPath of chartIndicatorPaths.controls; let i=index"
class="uk-visible-toggle uk-flex uk-margin-small-top" transition-group-item
[class.uk-active]="(!indicator.activePath && i == 0) || indicator.activePath === i">
<a (click)="activeChartIndicatorPath(i)">
<span>{{ getParameter(i, 'title')?.get('value')?.value ? getParameter(i, 'title').get('value').value : 'No title yet' }}</span>
</a>
<span *ngIf="!indicator.defaultId && chartIndicatorPaths.length > 1"
class="uk-flex uk-flex-column uk-flex-center uk-margin-small-left"
[class.uk-invisible-hover]="indicator.activePath !== i"
(click)="$event.stopPropagation();$event.preventDefault()">
<a *ngIf="isCurator" class="uk-link-reset uk-flex uk-flex-middle">
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0;">
<ul class="uk-nav uk-dropdown-nav">
<li *ngIf="i > 0">
<a (click)="hide(element);moveIndicatorPath(chartIndicatorFb, 'chart', i)">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" name="west" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">Move Left</span>
</div>
</a>
</li>
<li *ngIf="i < chartIndicatorPaths.length - 1">
<a (click)="hide(element);moveIndicatorPath(chartIndicatorFb, 'chart', i, i + 1)">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" name="east" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">Move Right</span>
</div>
</a>
</li>
<li>
<a (click)="removeChartIndicatorPath(i); hide(element)">
<div class="uk-flex uk-flex-middle">
<icon [flex]="true" name="delete" ratio="0.6"></icon>
<span class="uk-margin-small-left uk-width-expand">Delete</span>
</div>
</a>
</li>
</ul>
</div>
</span>
</li>
<li class="uk-margin-small-top">
<a (click)="activeChartIndicatorPath(chartIndicatorPaths.length); $event.preventDefault()"
class="uk-flex uk-flex-middle">
<icon name="add" [flex]="true"></icon>
<span class="uk-text-uppercase">Add</span>
</a>
</li>
</ul>
</div>
<div *ngFor="let indicatorPath of chartIndicatorPaths.controls; let i=index;"
[formGroupName]="i" class="uk-grid" uk-grid>
[formGroupName]="i">
<div *ngIf="(!indicator.activePath && i == 0) || indicator.activePath === i" class="uk-grid"
uk-grid>
<div class="uk-width-1-1 uk-flex uk-flex-middle">
<div input class="uk-width-expand" [title]="indicatorPath.get('url').disabled?'Default chart URLs cannot change':''"
<div input class="uk-width-expand"
[title]="indicatorPath.get('url').disabled?'Default chart URLs cannot change':''"
[formInput]="indicatorPath.get('url')" placeholder="Chart URL">
<div *ngIf="urlParameterizedMessage" warning>{{urlParameterizedMessage}}</div>
</div>
<div class='uk-padding-small'>
<a class="uk-link-reset" (click)="copyToClipboard(indicatorPath.get('url').value)"><icon [flex]="true" name="content_copy"></icon></a>
</div>
</div>
<div *ngIf="showCheckForSchemaEnhancements" class=" uk-width-1-1 ">
<div class="uk-alert uk-alert-warning">
There are schema enhancements that can be applied in this query. <a
(click)="indicatorPath.get('url').setValue(indicatorUtils.applySchemaEnhancements(indicatorPath.get('url').value)); indicatorPath.get('url').markAsDirty()">Apply
now</a>
<a class="uk-link-reset" (click)="copyToClipboard(indicatorPath.get('url').value)">
<icon [flex]="true" name="content_copy"></icon>
</a>
</div>
</div>
<div class="uk-width-1-1" formArrayName="parameters">
<div class="uk-grid" uk-grid>
<div *ngIf="getParameter(i, 'title')" input class="uk-width-1-1" [formInput]="getParameter(i, 'title').get('value')"
<div *ngIf="getParameter(i, 'title')" input class="uk-width-1-1"
[formInput]="getParameter(i, 'title').get('value')"
placeholder="Chart Title"></div>
<div *ngIf="getParameter(i, 'subtitle')" input class="uk-width-1-1" placeholder="Chart Subtitle" [formInput]="getParameter(i, 'subtitle').get('value')" label="Chart Subtitle"></div>
<div *ngIf="!getParameter(i, 'type')" input class="uk-width-1-3@s" [formInput]="indicatorPath.get('type')" placeholder="Chart Type"
<div *ngIf="getParameter(i, 'subtitle')" input class="uk-width-1-1"
placeholder="Chart Subtitle" [formInput]="getParameter(i, 'subtitle').get('value')"
label="Chart Subtitle"></div>
<div *ngIf="!getParameter(i, 'type')" input class="uk-width-1-3@s"
[formInput]="indicatorPath.get('type')" placeholder="Chart Type"
[options]="(indicatorPath.get('type').value == 'table' && getParameter(i, 'data_title_0'))?indicatorUtils.getChartTypes(indicatorPath.get('type').value):indicatorUtils.allChartTypes"
type="select"></div>
<div *ngIf="getParameter(i, 'type')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'type').get('value')" placeholder="Chart Type"
[options]="indicatorUtils.getChartTypes(getParameter(i, 'type').get('value').value)" type="select"></div>
<div *ngIf="getParameter(i, 'xAxisTitle')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'xAxisTitle').get('value')"
<div *ngIf="getParameter(i, 'type')" input class="uk-width-1-3@s"
[formInput]="getParameter(i, 'type').get('value')" placeholder="Chart Type"
[options]="indicatorUtils.getChartTypes(getParameter(i, 'type').get('value').value)"
type="select"></div>
<div *ngIf="getParameter(i, 'xAxisTitle')" input class="uk-width-1-3@s"
[formInput]="getParameter(i, 'xAxisTitle').get('value')"
placeholder="X-Axis Title"></div>
<div *ngIf="getParameter(i, 'yAxisTitle')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'yAxisTitle').get('value')"
<div *ngIf="getParameter(i, 'yAxisTitle')" input class="uk-width-1-3@s"
[formInput]="getParameter(i, 'yAxisTitle').get('value')"
placeholder="Y-Axis Title"></div>
<div *ngIf="getParameter(i, 'data_title_0')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'data_title_0').get('value')"
<div *ngIf="getParameter(i, 'data_title_0')" input class="uk-width-1-3@s"
[formInput]="getParameter(i, 'data_title_0').get('value')"
placeholder="Data Title"></div>
<div *ngIf="getParameter(i, 'data_title_1')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'data_title_1').get('value')"
<div *ngIf="getParameter(i, 'data_title_1')" input class="uk-width-1-3@s"
[formInput]="getParameter(i, 'data_title_1').get('value')"
placeholder="Data Title"></div>
<div *ngIf="getParameter(i, 'start_year')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'start_year').get('value')"
<div *ngIf="getParameter(i, 'start_year')" input class="uk-width-1-3@s"
[formInput]="getParameter(i, 'start_year').get('value')"
placeholder="Year (From)"></div>
<div *ngIf="getParameter(i, 'end_year')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'end_year').get('value')"
<div *ngIf="getParameter(i, 'end_year')" input class="uk-width-1-3@s"
[formInput]="getParameter(i, 'end_year').get('value')"
placeholder="Year (To)"></div>
<div *ngIf="getParameter(i, 'statsProfile') && statsProfiles" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'statsProfile').get('value')"
<div *ngIf="getParameter(i, 'statsProfile') && statsProfiles" input
class="uk-width-1-3@s" [formInput]="getParameter(i, 'statsProfile').get('value')"
[type]="'select'" [options]="statsProfiles" placeholder="Stats Profile"></div>
</div>
</div>
@ -411,7 +590,8 @@
<div *ngIf="(hasDifference(i)) && !indicatorPath.invalid"
class="uk-width-1-1 uk-height-large refresh-indicator">
<div class="uk-position-relative uk-height-1-1">
<a class="uk-position-center uk-text-center uk-link-reset" (click)="refreshIndicator()">
<a class="uk-position-center uk-text-center uk-link-reset"
(click)="refreshIndicator()">
<div>
<icon name="refresh"></icon>
</div>
@ -419,11 +599,15 @@
</a>
</div>
</div>
<iframe *ngIf="indicator.indicatorPaths[i].source !== 'image'" [class.uk-blend-multiply]="!isFullscreen"
[src]="indicator.indicatorPaths[i].safeResourceUrl" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"
<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="indicator.indicatorPaths[i].source === 'image'">
<img class="uk-width-1-1 uk-height-large uk-blend-multiply" [src]="indicator.indicatorPaths[i].url">
<img class="uk-width-1-1 uk-height-large uk-blend-multiply"
[src]="indicator.indicatorPaths[i].url">
</div>
</div>
</div>
</div>
@ -432,13 +616,15 @@
<div #editChartNotify notify-form class="uk-width-1-1 uk-margin-medium-top"></div>
</div>
</modal-alert>
<modal-alert #deleteModal (alertOutput)="deleteIndicator()" [overflowBody]="false" classTitle="uk-background-primary uk-light">
<modal-alert #deleteModal (alertOutput)="deleteIndicator()" [overflowBody]="false"
classTitle="uk-background-primary uk-light">
<div [class.uk-invisible]="editing" class="uk-position-relative">
<div *ngIf="editing">
<loading class="uk-position-center"></loading>
</div>
You are about to delete <span class="uk-text-bold" *ngIf="indicator && index !== -1">
"{{indicator.name ? indicator.name : (indicator.indicatorPaths[0]?.parameters?.title?indicator.indicatorPaths[0].parameters.title:'')}}"</span> indicator permanently.
"{{ indicator.name ? indicator.name : (indicator.indicatorPaths[0]?.parameters?.title ? indicator.indicatorPaths[0].parameters.title : '') }}
"</span> indicator permanently.
<div *ngIf="indicatorChildrenActionOnDelete == 'delete'" class="uk-text-bold">
Indicators of all profiles based on this default indicator, will be deleted as well.
</div>
@ -446,7 +632,8 @@
<div #deleteNotify notify-form class="uk-width-1-1 uk-margin-medium-top"></div>
</div>
</modal-alert>
<modal-alert #deleteSectionModal (alertOutput)="deleteSection()" [overflowBody]="false" classTitle="uk-background-primary uk-light">
<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">
<loading class="uk-position-center"></loading>

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,6 +83,8 @@ 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>();
@ -94,12 +97,13 @@ 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('numbersTransition') numbersTransition: TransitionGroupComponent;
@ViewChild('chartsTransition') chartsTransition: TransitionGroupComponent;
public isFullscreen: boolean = false;
@ -124,7 +128,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 = [];
@ -186,7 +189,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++) {
@ -194,12 +197,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 => {
@ -209,24 +207,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');
}));
});
}
@ -263,7 +269,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),
@ -275,13 +281,15 @@ 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]);
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]);
indexes.push([i, j, k]);
urls.set(pair, indexes);
});
});
});
this.numberSubscription.forEach(value => {
if (value instanceof Subscriber) {
value.unsubscribe();
@ -301,10 +309,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];
}
@ -317,7 +325,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
} else {
result = 0;
}
this.numberResults.set(i + '-' + j, result);
this.numberResults.set(i + '-' + j + '-' + k, result);
});
}
@ -364,6 +372,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';
@ -460,7 +476,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;
@ -496,14 +512,12 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
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)));
this.checkForSchemaEnhancements(this.numberIndicatorPaths.at(index).get('url').value);
if (this.indicator.indicatorPaths[index]) {
this.indicator.indicatorPaths[index] = indicatorPath;
} else {
@ -522,8 +536,6 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
}
this.getJsonPath(index).setValue(paths)
}
} else {
this.urlParameterizedMessage = null;
}
})
);
@ -559,11 +571,9 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
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);
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]) {
@ -573,13 +583,75 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
indicatorPath.safeResourceUrl = this.indicator.indicatorPaths[index].safeResourceUrl;
this.indicator.indicatorPaths[index] = indicatorPath;
}
} else {
this.urlParameterizedMessage = null;
}
}));
}
}
public removeNumberIndicatorPath(index: number) {
this.numberIndicatorPaths.removeAt(index);
this.indicator.indicatorPaths.splice(index, 1);
this.numbersTransition.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.chartsTransition.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,
type: 'number' | 'chart', index: number,
newIndex: number = index - 1) {
let indicatorPaths = type == 'number'?this.numberIndicatorPaths:this.chartIndicatorPaths;
if(type == 'number') {
this.numbersTransition.init();
} else {
this.chartsTransition.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.numbersTransition.init();
}
this.indicator.activePath = index;
}
public activeChartIndicatorPath(index: number) {
let paths = this.chartIndicatorPaths;
if (index == paths.length) {
this.addChartIndicatorPath();
this.chartsTransition.init();
}
this.indicator.activePath = index;
}
private getJsonPathAsFormArray(indicatorPath: IndicatorPath): UntypedFormArray {
let jsonPath = this.fb.array([]);
if (indicatorPath.jsonPath) {
@ -593,6 +665,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)) {
@ -613,7 +688,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) {
@ -655,26 +744,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();
@ -721,22 +810,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();
}
@ -836,7 +909,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;
@ -889,7 +962,23 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
this.loading = false;
}
reorderIndicators(sectionId: string, type: IndicatorType, reorder: Reorder) {
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,
@ -898,7 +987,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();
@ -912,7 +1001,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;
@ -921,7 +1010,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]) {
@ -942,12 +1031,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);
@ -1191,10 +1280,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
@ -1210,6 +1295,11 @@ 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;
}
@ -1217,7 +1307,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
importIndicatorsAndSave(stakeholder: Stakeholder, charts: any[]) {
let sectionsToSave: Section[] = [];
let countIndicators = 0;
if(stakeholder.type !== this.stakeholder.type) {
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,
@ -1230,23 +1320,25 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
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.";
}
@ -1261,29 +1353,38 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
}
if (chart.type == "chart") {
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)) {
duplicates++;
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, 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++;
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 (!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++;
}
@ -1315,37 +1416,42 @@ 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,
"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,
"url": this.indicatorUtils.getNumberUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)),
"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] = {
"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,
"url": this.getUrlByStakeHolder(indicatorPath),
"sectionTitle": section.title,
"sectionType": section.type,
"sectionIndex": index
};
indexIndicator++;
});
});
index++;
});
@ -1404,7 +1510,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
this.makeFileRequest(this.properties.utilsService + '/upload?type=json', [], this.filesToUpload).then(async (result: string) => {
let json = JSON.parse(result);
// validate file
if(json && Array.isArray(json)) {
if (json && Array.isArray(json)) {
UIkit.notification("This file is not supported any more. Please export indicators and try again!", {
status: 'danger',
timeout: 6000,
@ -1466,4 +1572,8 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
document.body.removeChild(tempBox);
NotificationHandler.rise('Copied to clipboard');
}
get isEditable(): boolean {
return this.stakeholder.copy || this.stakeholder.defaultId == null || this.stakeholder.defaultId == '-1';
}
}

View File

@ -28,12 +28,15 @@
</span>
<span class="uk-margin-xsmall-left hide-on-close" [class.uk-invisible-hover]="topicIndex !== i"
(click)="$event.stopPropagation();$event.preventDefault()">
<a class="uk-link-reset uk-flex uk-flex-middle">
<icon *ngIf="!isEditable && showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(topic.visibility)"
ratio="0.6"></icon>
<a *ngIf="isEditable && (isCurator || showVisibility)" class="uk-link-reset uk-flex uk-flex-middle">
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(topic.visibility)"
ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0; flip: false; container: body">
<div #element *ngIf="isEditable && (isCurator || showVisibility)"
uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0; flip: false; container: body">
<ul class="uk-nav uk-dropdown-nav">
<ng-container *ngIf="isCurator">
<li>
@ -106,12 +109,14 @@
<span class="uk-width-expand uk-text-truncate">{{category.name}}</span>
<span class="uk-margin-xsmall-left hide-on-close" [class.uk-invisible-hover]="categoryIndex !== j"
(click)="$event.stopPropagation();$event.preventDefault()">
<a class="uk-link-reset uk-flex uk-flex-middle">
<icon *ngIf="!isEditable && showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(category.visibility)"
ratio="0.6"></icon>
<a *ngIf="isEditable && (isCurator || showVisibility)" class="uk-link-reset uk-flex uk-flex-middle">
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(category.visibility)"
ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element
<div #element *ngIf="isEditable && (isCurator || showVisibility)"
uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0; flip: false; container: body">
<ul class="uk-nav uk-dropdown-nav">
<ng-container *ngIf="isCurator">
@ -175,7 +180,7 @@
</div>
</a>
</li>
<li *ngIf="isCurator">
<li *ngIf="isCurator && isEditable">
<a (click)="editCategoryOpen(); $event.preventDefault()" class="uk-flex uk-flex-middle">
<icon name="add" [flex]="true"></icon>
<span class="hide-on-close">Create new category</span>
@ -183,7 +188,7 @@
</li>
</ul>
</li>
<li *ngIf="isCurator" class="hide-on-close">
<li *ngIf="isCurator && isEditable" class="hide-on-close">
<a (click)="editTopicOpen(-1); $event.preventDefault()">
<div class="uk-flex uk-flex-middle">
<div class="uk-width-auto">
@ -217,10 +222,6 @@
[queryParams]="{view: 'RESTRICTED'}"
(click)="hide(element)">Restricted view</a>
</li>
<!--<li class="disabled"><a class="uk-disabled uk-text-muted"
uk-tooltip="Note: available only in administration dashboard"
(click)="hide(element)">Private view</a>
</li>-->
</ul>
</div>
</div>
@ -235,12 +236,15 @@
<span class="uk-flex uk-flex-column uk-flex-center uk-margin-small-left"
[class.uk-invisible-hover]="subCategoryIndex !== i"
(click)="$event.stopPropagation();$event.preventDefault()">
<a class="uk-link-reset uk-flex uk-flex-middle">
<icon *ngIf="!isEditable && showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(subCategory.visibility)"
ratio="0.6"></icon>
<a *ngIf="isEditable && (isCurator || showVisibility)" class="uk-link-reset uk-flex uk-flex-middle">
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(subCategory.visibility)"
ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0; container: body">
<div #element *ngIf="isEditable && (isCurator || showVisibility)"
uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0; container: body">
<ul class="uk-nav uk-dropdown-nav">
<ng-container *ngIf="isCurator">
<li>
@ -318,7 +322,7 @@
</span>
</li>
</ng-template>
<li *ngIf="isCurator">
<li *ngIf="isCurator && isEditable">
<a (click)="editSubCategoryOpen(); $event.preventDefault()" class="uk-flex uk-flex-middle">
<icon name="add" [flex]="true"></icon>
<span class="uk-text-uppercase">Create new subcategory</span>

View File

@ -359,6 +359,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);
};
@ -500,6 +502,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);
};
@ -640,6 +644,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);
};
@ -777,4 +783,8 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit,
this.visibilityModal.alertFooter = false;
this.visibilityModal.open();
}
get isEditable(): boolean {
return this.stakeholder.copy || this.stakeholder.defaultId == null || this.stakeholder.defaultId == '-1';
}
}

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

@ -7,16 +7,19 @@ import {
IndicatorPathType,
IndicatorType,
SourceType,
Stakeholder, SubCategory,
Topic,
Stakeholder,
Visibility,
} from "../../monitor/entities/stakeholder";
import {AbstractControl, ValidatorFn, Validators} from "@angular/forms";
import {Option} from "../../sharedComponents/input/input.component";
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';
@ -36,7 +39,13 @@ class Entities {
researchers = 'Researchers';
}
export interface OAIndicator {
numerator: IndicatorPath;
denominator: IndicatorPath;
}
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 +64,16 @@ 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;
public static openAccess: Map<string, OAIndicator> = new Map();
}
export class StakeholderUtils {
get roles() {
return StakeholderConfiguration.ROLES;
}
get entities() {
return StakeholderConfiguration.ENTITIES;
}
@ -82,6 +98,18 @@ export class StakeholderUtils {
return StakeholderConfiguration.CACHE_INDICATORS;
}
get hasMultiNumberIndicatorPaths() {
return StakeholderConfiguration.NUMBER_MULTI_INDICATOR_PATHS;
}
get hasMultiChartIndicatorPaths() {
return StakeholderConfiguration.CHART_MULTI_INDICATOR_PATHS;
}
get openAccess(): Map<string, OAIndicator> {
return StakeholderConfiguration.openAccess;
}
visibilityIcon: Map<Visibility, string> = new Map<Visibility, string>(this.visibilities.map(option => [option.value, option.icon]));
defaultValue(options: Option[]) {
@ -107,60 +135,6 @@ export class StakeholderUtils {
return types;
}
public createFunderFromDefaultProfile(funder: Stakeholder, defaultTopics: Topic[], isDefault: boolean = false): Stakeholder {
funder.topics = HelperFunctions.copy(defaultTopics);
for (let topic of funder.topics) {
topic.defaultId = !isDefault ? topic._id : null;
topic._id = null;
for (let category of topic.categories) {
category.defaultId = !isDefault ? category._id : null;
category._id = null;
let subTokeep: SubCategory[] = [];
for (let subCategory of category.subCategories) {
subCategory.defaultId = !isDefault ? subCategory._id : null;
subCategory._id = null;
subTokeep.push(subCategory);
for (let section of subCategory.charts) {
let chartsTokeep: Indicator[] = [];
section.defaultId = !isDefault ? section._id : null;
section.stakeholderAlias = funder.alias;
section._id = null;
for (let indicator of section.indicators) {
indicator.defaultId = !isDefault ? indicator._id : null;
indicator._id = null;
chartsTokeep.push(indicator);
for (let indicatorPath of indicator.indicatorPaths) {
if (indicatorPath.parameters) {
Object.keys(indicatorPath.parameters).forEach(key => {
if (key == "index_name") {
indicatorPath.parameters[key] = funder.index_name;
} else if (key == "index_id") {
indicatorPath.parameters[key] = funder.index_id;
} else if (key == "index_shortName") {
indicatorPath.parameters[key] = funder.index_shortName.toLowerCase();
}
});
}
}
}
section.indicators = chartsTokeep;
}
for (let section of subCategory.numbers) {
section.defaultId = !isDefault ? section._id : null;
section.stakeholderAlias = funder.alias;
section._id = null;
for (let indicator of section.indicators) {
indicator.defaultId = !isDefault ? indicator._id : null;
indicator._id = null;
}
}
}
category.subCategories = subTokeep;
}
}
return funder;
}
aliasValidatorString(elements: string[]): ValidatorFn {
return (control: AbstractControl): { [key: string]: string } | null => {
@ -588,11 +562,11 @@ export class IndicatorUtils {
filterApplied = true;
} else if (filterType == "start_year" || filterType == "end_year") {
//if has date filter already
if (filterType == "start_year" && parseInt(filterValue) > parseInt(queries["query"]["filters"][filterposition.filter]['groupFilters'][filterposition.groupFilter]["values"][0])) {
// if (filterType == "start_year" && parseInt(filterValue) > parseInt(queries["query"]["filters"][filterposition.filter]['groupFilters'][filterposition.groupFilter]["values"][0])) {
queries["query"]["filters"][filterposition.filter]['groupFilters'][filterposition.groupFilter]["values"][0] = filterValue;
} else if (filterType == "end_year" && parseInt(filterValue) < parseInt(queries["query"]["filters"][filterposition.filter]['groupFilters'][filterposition.groupFilter]["values"][0])) {
// } else if (filterType == "end_year" && parseInt(filterValue) < parseInt(queries["query"]["filters"][filterposition.filter]['groupFilters'][filterposition.groupFilter]["values"][0])) {
queries["query"]["filters"][filterposition.filter]['groupFilters'][filterposition.groupFilter]["values"][0] = filterValue;
}
// }
filterApplied = true;
}
} else {

View File

@ -37,6 +37,7 @@ export class Stakeholder {
isUpload: boolean = false;
description: string;
topics: any[];
copy: boolean = true;
details?: any;
constructor(_id: string, type: StakeholderType, index_id: string, index_name: string, index_shortName: string, alias: string, visibility: Visibility, logoUrl: string, defaultId: string = null, description: string = null) {
@ -50,6 +51,7 @@ export class Stakeholder {
this.visibility = visibility;
this.logoUrl = logoUrl;
this.description = description;
this.copy = !!this.defaultId && this.defaultId !== '-1';
this.topics = [];
}
@ -183,6 +185,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) {
@ -204,7 +207,7 @@ export class IndicatorPath {
type: IndicatorPathType;
source: SourceType;
url: string;
safeResourceUrl: SafeResourceUrl; // initialize on front end
safeResourceUrl?: SafeResourceUrl; // initialize on front end
jsonPath: string[];
chartObject: string;
parameters: any;
@ -293,7 +296,7 @@ export class IndicatorFilterUtils {
}
static getFieldForTable(field, table){
if(["publication", "software", "dataset", "other", "result"].indexOf(table)!=-1 && IndicatorFilterUtils.filteredFields[field]["result"]){
return IndicatorFilterUtils.filteredFields[field]["result"];
return IndicatorFilterUtils.filteredFields[field]["result"].replace("result.",table + ".");
}else{
return IndicatorFilterUtils.filteredFields[field][table];
}

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]);
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]);
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({
@ -111,11 +116,15 @@ export class StakeholderService {
}));
}
buildStakeholder(url: string, stakeholder: Stakeholder): Observable<Stakeholder> {
buildStakeholder(url: string, stakeholder: Stakeholder, copyId: string): Observable<Stakeholder> {
if (stakeholder.alias && stakeholder.alias.startsWith('/')) {
stakeholder.alias = stakeholder.alias.slice(1);
}
return this.http.post<Stakeholder>(url + '/build-stakeholder', stakeholder, CustomOptions.registryOptions()).pipe(map(stakeholder => {
let copy = {
stakeholder: stakeholder,
copyId: copyId
}
return this.http.post<Stakeholder>(url + '/build-stakeholder', copy, CustomOptions.registryOptions()).pipe(map(stakeholder => {
return HelperFunctions.copy(Stakeholder.checkIsUpload(stakeholder));
}));
}
@ -124,13 +133,12 @@ export class StakeholderService {
return this.http.post<Visibility>(url + '/' + path.join('/') + '/change-visibility' + '?visibility=' + visibility + (propagate ? '&propagate=true' : ''), null, CustomOptions.registryOptions());
}
saveElement(url: string, element: any, path: string[] = []): Observable<any> {
saveElement(url: string, element: any, path: string[] = [], isFull: boolean = false): Observable<any> {
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 => {
'/save' + (isFull?'/full':''), element, CustomOptions.registryOptions()).pipe(map(element => {
if (path.length === 0) {
return HelperFunctions.copy(Stakeholder.checkIsUpload(element));
} else {
@ -140,7 +148,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 +159,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 +166,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 +174,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

@ -1,4 +1,4 @@
import {Component, Input, ViewChild} from '@angular/core';
import {ChangeDetectorRef, Component, Input, ViewChild} from '@angular/core';
import {ActivatedRoute, Router} from "@angular/router";
import {Subscriber, Subscription} from "rxjs";
import {OrcidService} from "./orcid.service";
@ -85,7 +85,7 @@ declare var UIkit: any;
<ng-container *ngIf="!showOnlyUpdateButton">
<span class="uk-margin-bottom uk-flex uk-flex-middle uk-flex-center">
<span>ORCID&#160;</span>
<img src="assets/common-assets/common/ORCIDiD_icon16x16.png" alt="" loading="lazy">
<img src="assets/common-assets/common/ORCIDiD_icon16x16.png" alt="ORCID logo" loading="lazy">
<span>&#160;ACTIONS</span>
</span>
@ -330,7 +330,8 @@ export class OrcidWorkComponent {
private _router: Router,
private orcidService: OrcidService,
private resultLandingService: ResultLandingService,
private userManagementService: UserManagementService, private _logService: LogService, private _userProfileService: UserProfileService) {
private userManagementService: UserManagementService, private _logService: LogService, private _userProfileService: UserProfileService,
private cdr: ChangeDetectorRef) {
if (typeof document !== 'undefined') {
this.tokenUrl = properties.orcidTokenURL
+ "client_id=" + properties.orcidClientId
@ -444,6 +445,8 @@ export class OrcidWorkComponent {
this.subscriptions.push(this.orcidService.getPutCode(this.pids).subscribe(
putCodes => {
this.putCodes = putCodes;
this.cdr.markForCheck();
// this.cdr.detectChanges();
}, error => {
}
@ -525,6 +528,8 @@ export class OrcidWorkComponent {
this.putCodes.push("" + response['put-code']);
this.creationDates.push(response['created-date']['value']);
this.updateDates.push(response['last-modified-date']['value']);
this.cdr.markForCheck();
// this.cdr.detectChanges();
// this.closeGrantModal();
// this.message = "You have successfully added work with pids: "+this.pids+" in your ORCID record!";
@ -701,6 +706,8 @@ export class OrcidWorkComponent {
deletedAll = false;
} else {
this.putCodes.splice(i, 1);
this.cdr.markForCheck();
// this.cdr.detectChanges();
this.creationDates.splice(i, 1);
this.updateDates.splice(i, 1);
// this.works.splice(i, 1);

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>
You have been invited to join <span class="uk-text-bold">{{name}}</span> Monitor Dashboard as a member.
<div>
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>
@ -83,7 +73,7 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
public id: string;
@Input()
set type(type: string) {
this._type = Role.GROUP + type;
this._type = Role.GROUP + Role.mapType(type);
}
@Input()
public name: string;
@ -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,7 +97,7 @@ 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,
@ -123,12 +115,17 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
}
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,11 +152,8 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
relativeTo: this.relativeTo
});
}
} else if(this.isMember) {
this.openMemberModal();
}
} else {
this.isMember = false;
this.cdr.detectChanges();
}
});
@ -179,23 +173,18 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
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.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();
}
@ -214,16 +203,9 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
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 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(() => {
@ -244,7 +226,6 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
window.location.href = properties.adminPortalURL + '/' + this.verification.entity;
}));
}
});
}, error => {
this.loading = false;
this.error = 'The verification code is invalid';
@ -253,25 +234,16 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
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.memberModal.cancel();
this.loading = false;
this.error = null;
this.isMember = true;
this.userManagementService.updateUserInfo(() => {
if (this.paramsSubscription instanceof Subscription) {
this.paramsSubscription.unsubscribe();
}
this.cancel();
});
window.location.href = window.location.href.split('?')[0];
}, error => {
this.loading = false;
this.error = 'The verification code is invalid';
}));
} else {
this.memberModal.cancel();
}
}
public reset() {
@ -279,7 +251,6 @@ export class RoleVerificationComponent extends BaseComponent implements OnInit,
}
cancel() {
this.isMember = false;
this._router.navigate([]);
}
}

View File

@ -34,9 +34,9 @@
</div>
</div>
<div class="uk-width-2-5@m uk-width-1-1@s uk-text-center uk-position-relative" uk-scrollspy-class>
<img src="assets/common-assets/sdg/sdg-hero-img.svg" loading="lazy">
<img src="assets/common-assets/sdg/sdg-hero-img.svg" loading="lazy" alt="SDGs logo">
<div class="uk-position-bottom-left uk-card uk-card-default uk-padding">
<img src="assets/common-assets/sdg/sdg-badge.png" loading="lazy" style="max-width: 215px">
<img src="assets/common-assets/sdg/sdg-badge.png" loading="lazy" style="max-width: 215px" alt="SDGs big logo">
</div>
</div>
</div>
@ -61,7 +61,7 @@
</div>
</div>
<div class="uk-text-center">
<img [src]="'assets/common-assets/sdg/icons/g' + sdg.code + '.png'" alt="">
<img [src]="'assets/common-assets/sdg/icons/g' + sdg.code + '.png'" [alt]="'SDG code: '+sdg.code">
</div>
<div class="uk-text-center uk-padding-small uk-background-default" style="border-radius: 0 0 4px 4px;">
<span class="uk-text-large uk-text-bold uk-margin-small-bottom">{{sdg.number == null ? '0' : sdg.number | number}}</span>

View File

@ -33,6 +33,7 @@ import {RefineFieldResultsService} from "../services/refineFieldResults.service"
[includeOnlyResultsAndFilter]="includeOnlyResultsAndFilter"
[searchForm]="searchForm"
[sort]="false"
[showRefine]="refineFields?.length > 0"
[filters]="filters"
[simpleView]="simpleView" formPlaceholderText="Search by organization name..."
[showSwitchSearchLink]="showSwitchSearchLink"

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-secondary 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-secondary 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-secondary 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-secondary 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,15 @@
</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">-->
<!-- </div>-->
<div *ngIf="!mobile && showRefine
&& (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,10 +316,31 @@
[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-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"
@ -319,17 +348,12 @@
</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-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>
@ -353,12 +377,19 @@
</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")
) {
@ -78,6 +78,7 @@ export class SearchResultComponent implements OnInit, OnChanges {
// console.debug(i, this.previewResults[i].orcidPutCodes);
}
}
// this.previewResults = JSON.parse(JSON.stringify(this.previewResults, this.replacer), this.reviver);
}, error => {
}
@ -85,6 +86,26 @@ export class SearchResultComponent implements OnInit, OnChanges {
}
}
private replacer(key, value) {
if(value instanceof Map) {
return {
dataType: 'Map',
value: Array.from(value.entries()), // or with spread: value: [...value]
};
} else {
return value;
}
}
private reviver(key, value) {
if(typeof value === 'object' && value !== null) {
if (value.dataType === 'Map') {
return new Map(value.value);
}
}
return value;
}
public getResultPreview(result: SearchResult): ResultPreview {
return ResultPreview.searchResultConvert(result, (result.entityType) ? result.entityType : this.type);
}

View File

@ -231,6 +231,7 @@ export class SearchProjectsService {
let fundingData = Array.isArray(resData['fundingtree']) ? resData['fundingtree'][z] : resData['fundingtree'];
if(fundingData.hasOwnProperty("funder")) {
result['funderShortname'] = fundingData['funder'].shortname;
result['funderName'] = fundingData['funder'].name;
result['funderId'] = fundingData['funder'].id;
result['jurisdiction'] = (fundingData['funder']['id']['jurisdiction'] )?fundingData['funder']['id']['jurisdiction']:"";

View File

@ -70,7 +70,7 @@ export class RefineResultsUtils {
}
public static inParenthesisThePartAfterCharacters(field, characters):string {
if( field.name.indexOf(characters) !=-1){
return field.name.split(characters)[0]+" ("+field.name.split(characters)[1]+")";
return field.name.split(characters)[0]+ (field.name.split(characters)[1]?(" ("+field.name.split(characters)[1]+")"):"");
}
return field.name;

View File

@ -0,0 +1,7 @@
import {ActivatedRouteSnapshot, BaseRouteReuseStrategy} from "@angular/router";
export class CustomRouteReuseStrategy extends BaseRouteReuseStrategy {
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig || (!!future.routeConfig?.component && future.routeConfig?.component === curr.routeConfig?.component)
}
}

View File

@ -101,7 +101,7 @@
<li><a
[href]="'https://'+(properties.environment =='beta' || properties.environment =='development'?'beta.':'')+'monitor.openaire.eu'"
target="_blank">Monitor</a></li>
<li><a href="https://graph.openaire.eu/develop/" target="_blank">Develop</a></li>
<li><a href="'https://'+(properties.environment =='beta' || properties.environment =='development'?'beta.':'')+'develop.openaire.eu" target="_blank">Develop</a></li>
</ul>
</div>
</div>

View File

@ -12,7 +12,7 @@
Send a message
</div>
<div *ngIf="images.length > 0" class="uk-margin-small avatars">
<img *ngFor="let image of images" [src]="image" class="uk-border-circle" >
<img *ngFor="let image of images" [src]="image" class="uk-border-circle" alt="contact person">
</div>
<div class="uk-text-bold">
How can we help?

View File

@ -19,7 +19,7 @@ import {properties} from "../../../../environments/environment";
@Component({
selector: 'showAuthors',
template: `
<ng-template #author_template let-author="author" let-i="i" let-italic="italic">
<ng-template #author_template let-author="author" let-i="i" let-italic="italic" let-modal="modal">
<span *ngIf="isSticky || (!author.orcid && !author.orcid_pending) || !isBrowser" style="margin-right: 5px;"
[class.uk-text-italic]="italic">
{{author.fullName + ";"}}
@ -36,7 +36,7 @@ import {properties} from "../../../../environments/environment";
</span>
</a>
<div *ngIf="!isMobile" class="default-dropdown uk-margin-remove-top uk-dropdown orcid-dropdown"
uk-dropdown="mode:click; offset: 4;" style="min-width: 465px !important;" [attr.container]="isModal?'#modal-container':true">
uk-dropdown="mode:click; offset: 4; pos: bottom-right" style="min-width: 465px !important;" [attr.container]="'#modal-container'">
<ng-container *ngTemplateOutlet="dropdown"></ng-container>
</div>
<mobile-dropdown *ngIf="isMobile" [toggle]="toggle">
@ -112,7 +112,7 @@ import {properties} from "../../../../environments/environment";
</span>
<ng-container *ngFor="let author of authors.slice(0, viewAll?authors.length:authorsLimit) let i=index">
<ng-container
*ngTemplateOutlet="author_template; context: { author: author, i:i, italic: true}"></ng-container>
*ngTemplateOutlet="author_template; context: { author: author, i:i, italic: true, modal: modal}"></ng-container>
</ng-container>
<span *ngIf="!showAll && authors && authors.length > authorsLimit" class="uk-text-meta">
+{{authors.length - authorsLimit | number}} more
@ -127,18 +127,18 @@ import {properties} from "../../../../environments/environment";
</div>
</div>
<modal-alert *ngIf="!isMobile" #authorsModal>
<modal-alert *ngIf="!isMobile && showAll && authors && authors.length > authorsLimit" #authorsModal>
<div class="uk-text-small uk-text-emphasis uk-grid uk-grid-column-collapse uk-grid-row-small" uk-grid>
<ng-container *ngFor="let author of authors; let i=index">
<ng-container *ngTemplateOutlet="author_template; context: { author: author, i:i}"></ng-container>
<ng-container *ngTemplateOutlet="author_template; context: { author: author, i:i, modal: authorsModal}"></ng-container>
</ng-container>
</div>
</modal-alert>
<fs-modal *ngIf="isMobile" #authorsModal classTitle="uk-tile-default uk-border-bottom">
<fs-modal *ngIf="isMobile && showAll && authors && authors.length > authorsLimit" #authorsModal classTitle="uk-tile-default uk-border-bottom">
<div class="uk-text-small uk-text-emphasis uk-grid uk-grid-column-collapse uk-grid-row-small" uk-grid>
<ng-container *ngFor="let author of authors; let i=index">
<ng-container *ngTemplateOutlet="author_template; context: { author: author, i:i}"></ng-container>
<ng-container *ngTemplateOutlet="author_template; context: { author: author, i:i, modal: authorsModal}"></ng-container>
</ng-container>
</div>
</fs-modal>
@ -153,7 +153,6 @@ export class ShowAuthorsComponent {
@Input() authorsLimit: number = 7;
@Input() showAll: boolean = true;
@Input() modal: AlertModal;
@Input() isModal: boolean
@Input() viewAll: boolean = false;
@Input() showInline: boolean = false; // do not open modal for "view more" when this is true
public lessBtn: boolean = false;
@ -175,6 +174,9 @@ export class ShowAuthorsComponent {
if (this.modal) {
this.modal.cancel();
}
if(this.authorsModal) {
this.authorsModal.cancel();
}
}
public viewAllClick() {

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";
@ -107,6 +108,20 @@ export class Composer {
return email;
}
public static composeEmailForDevelopPersonalInfo(user: User): Email {
let email: Email = new Email();
email.subject = "Welcome to The OpenAIRE API mailing list";
email.body = "<div style='font-size:" + this.noteBodySize + "'>"
+ "<p>Dear " + user.fullname + ",</p>"
+ "<p>As a user of the OpenAIRE APIs, you are a member of the openaire-api mailing list. " +
" We will use this list to inform you of relevant news and updates. If you wish to unsubscribe, " +
" please send an email to the list admin at <a href='mailto:gbikas@openaire.eu'>gbikas@openaire.eu</a> " +
" and <a href='mailto:stefania.amodeo@openaire.eu'>stefania.amodeo@openaire.eu</a>.</p>"
+ "<p>The OpenAIRE Graph team</p>";
email.recipients = [user.email];
return email;
}
public static composeEmailForDevelop(contactForm: any, admins: any, user: User): Email {
let email: Email = new Email();
email.subject = "OpenAIRE - Develop [" + properties.environment.toUpperCase() + "]";
@ -335,13 +350,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

@ -48,6 +48,7 @@ export class SearchResult {
acronym: string;
code: string;
// callIdentifier?: string; // currently not parsed
funderName: string;
funderShortname: string;
budget?: string;
contribution?: string;

View File

@ -237,7 +237,8 @@ private fetch (link,id,oafEntityType,type, properties:EnvProperties ){
if(resData['fundingtree'] && resData['fundingtree']['funder']){
value.funderId = (resData['fundingtree']['funder']['id'] )?resData['fundingtree']['funder']['id']:"";
value.funderName = (resData['fundingtree']['funder']['shortname'] )?resData['fundingtree']['funder']['shortname']:"";
value.funderName = (resData['fundingtree']['funder']['name'] )?resData['fundingtree']['funder']['name']:"";
value.funderShortName = (resData['fundingtree']['funder']['shortname'] )?resData['fundingtree']['funder']['shortname']:"";
value.jurisdiction = (resData['fundingtree']['funder']['jurisdiction'] )?resData['fundingtree']['funder']['jurisdiction']:"";
if(resData['fundingtree']['funding_level_2']){
value.fundingLevel0 = (resData['fundingtree']['funding_level_2'] && resData['fundingtree']['funding_level_2']['parent'] &&

View File

@ -56,6 +56,7 @@ export class MobileDropdownComponent implements OnInit{
parent.removeChild(this.element.nativeElement);
container.append(this.element.nativeElement);
this.opened = true;
this.cdr.markForCheck();
body.setAttribute('style', 'overflow-y: hidden');
}
}

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

@ -797,8 +797,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},
@ -818,7 +818,7 @@ export class SearchFieldsBase {
equalityOperator: "=",
filterType: null
},
["country"]: {
["countrynojurisdiction"]: {
name: "Country",
type: "vocabulary",
param: "country",
@ -826,6 +826,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"];
@ -961,7 +962,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

@ -95,12 +95,12 @@
<!-- 2nd section (funders, budget, authors, PIDs, publisher etc.) -->
<div class="uk-text-small uk-margin-small-bottom">
<!-- Funder -->
<div *ngIf="result.funderShortname || result.code" class="uk-margin-xsmall-bottom">
<span *ngIf="result.funderShortname">
<div *ngIf="(result.funderShortname || result.funderName) || result.code" class="uk-margin-xsmall-bottom">
<span *ngIf="result.funderShortname || result.funderName">
<span class="uk-text-meta">Funder: </span>
{{result.funderShortname}}
{{result.funderName ? result.funderName : result.funderShortname}}
</span>
<span *ngIf="result.code" [class.uk-margin-left]="result.funderShortname">
<span *ngIf="result.code" [class.uk-margin-left]="result.funderShortname || result.funderName">
<span class="uk-text-meta">{{openaireEntities.PROJECT}} Code: </span>
{{result.code}}
</span>
@ -125,7 +125,7 @@
</div>
<!-- Authors -->
<div *ngIf="result.authors" class="uk-margin-xsmall-bottom">
<showAuthors [authors]="result.authors" [authorsLimit]=4 [modal]="modal" [isMobile]="isMobile" [isModal]="isDeletedByInferenceModal"
<showAuthors [authors]="result.authors" [authorsLimit]=4 [modal]="modal" [isMobile]="isMobile"
[showAll]=true [showInline]="showInline"></showAuthors>
</div>
<!-- Identifiers -->

View File

@ -1,4 +1,4 @@
import {Component, Input, OnChanges, OnInit, SimpleChanges} from "@angular/core";
import {ChangeDetectionStrategy, Component, Input, OnChanges, OnInit, SimpleChanges} from "@angular/core";
import {ResultPreview} from "./result-preview";
import {EnvProperties} from "../properties/env-properties";
import {RouterHelper} from "../routerHelper.class";
@ -12,7 +12,8 @@ import {NumberUtils} from '../number-utils.class';
@Component({
selector: 'result-preview',
templateUrl: 'result-preview.component.html',
styleUrls: ['result-preview.component.less']
styleUrls: ['result-preview.component.less'],
// changeDetection: ChangeDetectionStrategy.OnPush
})
export class ResultPreviewComponent implements OnInit, OnChanges {
@Input() prevPath: string = "";
@ -255,4 +256,8 @@ export class ResultPreviewComponent implements OnInit, OnChanges {
}
return obj;
}
public get countDots() {
return (this.result.oaRoutes.green ? 1 : 0) + (this.result.oaRoutes.oaColor ? 1 : 0) + (this.result.oaRoutes.isInDiamondJournal ? 1 : 0);
}
}

View File

@ -123,6 +123,7 @@ export class ResultPreview {
acronym: string;
code: string;
// callIdentifier: string; // currently not parsed
funderName: string;
funderShortname: string;
budget: string;
contribution: string;
@ -188,6 +189,7 @@ export class ResultPreview {
resultPreview.acronym = result.acronym;
resultPreview.code = result.code;
// resultPreview.callIdentifier = result.callIdentifier; // currently not parsed
resultPreview.funderName = result.funderName;
resultPreview.funderShortname = result.funderShortname;
resultPreview.budget = result.budget;
resultPreview.contribution = result.contribution;

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,15 @@ export class TransitionGroupComponent implements AfterViewInit, OnDestroy {
@ContentChildren(TransitionGroupItemDirective) items: QueryList<TransitionGroupItemDirective>;
@Input()
public id: string;
public size: number;
private disabled: boolean = false;
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 && !this.disabled) {
items.forEach(item => item.prevPos = item.newPos || item.prevPos);
items.forEach(this.runCallback);
this.refreshPosition('newPos');
@ -80,6 +80,7 @@ export class TransitionGroupComponent implements AfterViewInit, OnDestroy {
init() {
this.refreshPosition('prevPos');
this.refreshPosition('newPos');
this.size = this.items.length;
}
runCallback(item: TransitionGroupItemDirective) {