Merging explore-redesign branch into develop for Explore July release #7
|
@ -0,0 +1,200 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,86 +1,103 @@
|
|||
import {properties} from "../../../../environments/environment";
|
||||
|
||||
export class Layout {
|
||||
_id:string;
|
||||
portalPid:string;
|
||||
layoutOptions:CustomizationOptions;
|
||||
_id: string;
|
||||
portalPid: string;
|
||||
layoutOptions: CustomizationOptions;
|
||||
date;
|
||||
constructor(community, options:CustomizationOptions){
|
||||
|
||||
constructor(community, options: CustomizationOptions) {
|
||||
this.portalPid = community;
|
||||
this.layoutOptions = options;
|
||||
}
|
||||
|
||||
public static getVariables(options: CustomizationOptions): {} | null {
|
||||
if(options) {
|
||||
if (options) {
|
||||
let variables = {};
|
||||
if(options.identity) {
|
||||
variables['@global-primary-background'] = options.identity.mainColor;
|
||||
variables['@global-secondary-background'] = options.identity.secondaryColor;
|
||||
if (options.identity) {
|
||||
variables['@global-primary-background'] = Layout.convertRGBAtoRGB(options.identity.mainColor);
|
||||
variables['@global-secondary-background'] = Layout.convertRGBAtoRGB(options.identity.secondaryColor);
|
||||
}
|
||||
if(options.backgrounds){
|
||||
variables['@general-search-form-background'] = options.backgrounds.form.color;
|
||||
variables['@base-body-background'] = options.backgrounds.light.color;
|
||||
variables['@hero-background-image'] = (options.backgrounds.form.imageFile?(this.getUrl(properties.utilsService + '/download/' +options.backgrounds.form.imageFile)): 'none') ;
|
||||
if (options.backgrounds) {
|
||||
variables['@general-search-form-background'] = Layout.convertRGBAtoRGB(options.backgrounds.form.color);
|
||||
variables['@global-background'] = Layout.convertRGBAtoRGB(options.backgrounds.light.color);
|
||||
variables['@hero-background-image'] = (options.backgrounds.form.imageFile ? (this.getUrl(properties.utilsService + '/download/' + options.backgrounds.form.imageFile)) : 'none');
|
||||
variables['@hero-background-position'] = options.backgrounds.form.position;
|
||||
variables['@hero-fonts-mode'] = options.backgrounds.form.fontsDarkMode == true ?'dark':'light';
|
||||
}
|
||||
if(options.buttons){
|
||||
if (options.buttons) {
|
||||
//general
|
||||
variables['@button-border-width'] = options.buttons.lightBackground.borderWidth + "px";
|
||||
variables['@button-border-radius'] = options.buttons.lightBackground.borderRadius + "px";
|
||||
// default -> on dark background todo check again when we have sucj=h buttons
|
||||
variables['@button-default-background'] = options.buttons.darkBackground.backgroundColor;
|
||||
variables['@button-default-color'] = options.buttons.darkBackground.color;
|
||||
variables['@button-default-border'] = options.buttons.darkBackground.borderColor;
|
||||
variables['@button-default-hover-background'] = options.buttons.darkBackground.onHover.backgroundColor;
|
||||
variables['@button-default-hover-color'] = options.buttons.darkBackground.onHover.color;
|
||||
variables['@button-default-hover-border'] = options.buttons.darkBackground.onHover.borderColor;
|
||||
variables['@button-default-active-background'] = options.buttons.darkBackground.onHover.backgroundColor;
|
||||
variables['@button-default-active-color'] = options.buttons.darkBackground.onHover.color;
|
||||
variables['@button-default-active-border'] = options.buttons.darkBackground.onHover.borderColor;
|
||||
variables['@button-default-background'] = Layout.convertRGBAtoRGB(options.buttons.darkBackground.backgroundColor);
|
||||
variables['@button-default-color'] = Layout.convertRGBAtoRGB(options.buttons.darkBackground.color);
|
||||
variables['@button-default-border'] = Layout.convertRGBAtoRGB(options.buttons.darkBackground.borderColor);
|
||||
variables['@button-default-hover-background'] = Layout.convertRGBAtoRGB(options.buttons.darkBackground.onHover.backgroundColor);
|
||||
variables['@button-default-hover-color'] = Layout.convertRGBAtoRGB(options.buttons.darkBackground.onHover.color);
|
||||
variables['@button-default-hover-border'] = Layout.convertRGBAtoRGB(options.buttons.darkBackground.onHover.borderColor);
|
||||
variables['@button-default-active-background'] = Layout.convertRGBAtoRGB(options.buttons.darkBackground.onHover.backgroundColor);
|
||||
variables['@button-default-active-color'] = Layout.convertRGBAtoRGB(options.buttons.darkBackground.onHover.color);
|
||||
variables['@button-default-active-border'] = Layout.convertRGBAtoRGB(options.buttons.darkBackground.onHover.borderColor);
|
||||
|
||||
// primary
|
||||
variables['@button-primary-background'] = options.buttons.lightBackground.backgroundColor;
|
||||
variables['@button-primary-color'] = options.buttons.lightBackground.color;
|
||||
variables['@button-primary-border'] = options.buttons.lightBackground.borderColor;
|
||||
variables['@button-primary-hover-background'] = options.buttons.lightBackground.onHover.backgroundColor;
|
||||
variables['@button-primary-hover-color'] = options.buttons.lightBackground.onHover.color;
|
||||
variables['@button-primary-hover-border'] = options.buttons.lightBackground.onHover.borderColor;
|
||||
variables['@button-primary-active-background'] = options.buttons.lightBackground.onHover.backgroundColor;
|
||||
variables['@button-primary-active-color'] = options.buttons.lightBackground.onHover.color;
|
||||
variables['@button-primary-active-border'] = options.buttons.lightBackground.onHover.borderColor;
|
||||
variables['@button-primary-background'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.backgroundColor);
|
||||
variables['@button-primary-color'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.color);
|
||||
variables['@button-primary-border'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.borderColor);
|
||||
variables['@button-primary-hover-background'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.onHover.backgroundColor);
|
||||
variables['@button-primary-hover-color'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.onHover.color);
|
||||
variables['@button-primary-hover-border'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.onHover.borderColor);
|
||||
variables['@button-primary-active-background'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.onHover.backgroundColor);
|
||||
variables['@button-primary-active-color'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.onHover.color);
|
||||
variables['@button-primary-active-border'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.onHover.borderColor);
|
||||
|
||||
// secondary
|
||||
variables['@button-secondary-background'] = options.buttons.lightBackground.color;
|
||||
variables['@button-secondary-color'] = options.buttons.lightBackground.backgroundColor;
|
||||
variables['@button-secondary-border'] = options.buttons.lightBackground.backgroundColor;
|
||||
variables['@button-secondary-hover-background'] = options.buttons.lightBackground.backgroundColor;
|
||||
variables['@button-secondary-hover-color'] = options.buttons.lightBackground.color;
|
||||
variables['@button-secondary-hover-border'] = options.buttons.lightBackground.borderColor;
|
||||
variables['@button-secondary-active-background'] = options.buttons.lightBackground.backgroundColor;
|
||||
variables['@button-secondary-active-color'] = options.buttons.lightBackground.color;
|
||||
variables['@button-secondary-active-border'] = options.buttons.lightBackground.borderColor;
|
||||
|
||||
variables['@button-secondary-background'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.color);
|
||||
variables['@button-secondary-color'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.backgroundColor);
|
||||
variables['@button-secondary-border'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.backgroundColor);
|
||||
variables['@button-secondary-hover-background'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.backgroundColor);
|
||||
variables['@button-secondary-hover-color'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.color);
|
||||
variables['@button-secondary-hover-border'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.borderColor);
|
||||
variables['@button-secondary-active-background'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.backgroundColor);
|
||||
variables['@button-secondary-active-color'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.color);
|
||||
variables['@button-secondary-active-border'] = Layout.convertRGBAtoRGB(options.buttons.lightBackground.borderColor);
|
||||
|
||||
}
|
||||
return variables;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static getUrl(url){
|
||||
|
||||
public static getUrl(url) {
|
||||
return 'url("' + url + '")';
|
||||
}
|
||||
|
||||
|
||||
public static convertRGBAtoRGB(color: string): string {
|
||||
if(color.includes('rgba')) {
|
||||
const regexPattern = /rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/;
|
||||
const matches = color.match(regexPattern);
|
||||
const [, r, g, b, a] = matches;
|
||||
let R = parseInt(r)*parseFloat(a) + (1 - parseFloat(a))*255;
|
||||
let G = parseInt(g)*parseFloat(a) + (1 - parseFloat(a))*255;
|
||||
let B = parseInt(b)*parseFloat(a) + (1 - parseFloat(a))*255;
|
||||
return 'rgb(' + R + ',' + G + ',' + B + ',' + ')';
|
||||
} else {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CustomizationOptions {
|
||||
identity: {
|
||||
mainColor: string;
|
||||
secondaryColor: string;
|
||||
};
|
||||
identityIsCustom:boolean;
|
||||
backgroundsIsCustom:boolean;
|
||||
buttonsIsCustom:boolean;
|
||||
identityIsCustom: boolean;
|
||||
backgroundsIsCustom: boolean;
|
||||
buttonsIsCustom: boolean;
|
||||
backgrounds: {
|
||||
dark: {
|
||||
color: string; //background
|
||||
color: string; //background
|
||||
}
|
||||
light: {
|
||||
color: string; //background
|
||||
|
@ -89,41 +106,43 @@ export class CustomizationOptions {
|
|||
color: string; //background
|
||||
imageUrl: string;
|
||||
imageFile: string;
|
||||
position:string;
|
||||
position: string;
|
||||
fontsDarkMode:boolean;
|
||||
}
|
||||
};
|
||||
buttons: {
|
||||
darkBackground: ButtonsCustomization;
|
||||
lightBackground: ButtonsCustomization;
|
||||
};
|
||||
|
||||
|
||||
constructor(mainColor: string = null, secondaryColor: string = null) {
|
||||
this.identity= {
|
||||
this.identity = {
|
||||
mainColor: mainColor ? mainColor : CustomizationOptions.getIdentity().mainColor,
|
||||
secondaryColor : secondaryColor ? secondaryColor : CustomizationOptions.getIdentity().secondaryColor,
|
||||
secondaryColor: secondaryColor ? secondaryColor : CustomizationOptions.getIdentity().secondaryColor,
|
||||
};
|
||||
this.identityIsCustom = false;
|
||||
this.backgroundsIsCustom = false;
|
||||
this.buttonsIsCustom = false;
|
||||
this.backgrounds={
|
||||
dark : {
|
||||
this.backgrounds = {
|
||||
dark: {
|
||||
color: this.identity.mainColor,
|
||||
},
|
||||
light : {
|
||||
light: {
|
||||
color: "#f9f9f9" //CustomizationOptions.getRGBA(this.identity.mainColor,0.05),
|
||||
},
|
||||
form : {
|
||||
color: CustomizationOptions.getRGBA(this.identity.mainColor,0.15),
|
||||
imageUrl : null,
|
||||
imageFile : null,
|
||||
position: null
|
||||
form: {
|
||||
color: CustomizationOptions.getRGBA(this.identity.mainColor, 0.15),
|
||||
imageUrl: null,
|
||||
imageFile: null,
|
||||
position: null,
|
||||
fontsDarkMode: true
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
this.buttons = {
|
||||
darkBackground: {
|
||||
isDefault:true,
|
||||
isDefault: true,
|
||||
backgroundColor: "#ffffff",
|
||||
color: "#000000",
|
||||
borderStyle: "solid",
|
||||
|
@ -137,7 +156,7 @@ export class CustomizationOptions {
|
|||
}
|
||||
},
|
||||
lightBackground: {
|
||||
isDefault:true,
|
||||
isDefault: true,
|
||||
backgroundColor: this.identity.mainColor,
|
||||
color: '#ffffff',
|
||||
borderStyle: "solid",
|
||||
|
@ -150,15 +169,19 @@ export class CustomizationOptions {
|
|||
borderColor: this.identity.secondaryColor,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
public static checkForObsoleteVersion(current:CustomizationOptions, communityId:string){
|
||||
let defaultCO = new CustomizationOptions(CustomizationOptions.getIdentity(communityId).mainColor,CustomizationOptions.getIdentity(communityId).secondaryColor);
|
||||
|
||||
public static checkForObsoleteVersion(current: CustomizationOptions, communityId: string) {
|
||||
if(communityId === 'connect') {
|
||||
return null;
|
||||
}
|
||||
let defaultCO = new CustomizationOptions(CustomizationOptions.getIdentity(communityId).mainColor, CustomizationOptions.getIdentity(communityId).secondaryColor);
|
||||
let updated = Object.assign({}, defaultCO);
|
||||
if(!current){
|
||||
if (!current) {
|
||||
current = Object.assign({}, defaultCO);
|
||||
}else {
|
||||
} else {
|
||||
if (current.identity && current.identity.mainColor && current.identity.secondaryColor) {
|
||||
updated = new CustomizationOptions(current.identity.mainColor, current.identity.secondaryColor);
|
||||
}
|
||||
|
@ -177,6 +200,9 @@ export class CustomizationOptions {
|
|||
if (!current.backgrounds.form.position) {
|
||||
current.backgrounds.form.position = "center bottom"
|
||||
}
|
||||
if (current.backgrounds.form.fontsDarkMode == undefined) {
|
||||
current.backgrounds.form.fontsDarkMode = true;
|
||||
}
|
||||
if (!current.buttons) {
|
||||
current.buttons = Object.assign({}, updated.buttons);
|
||||
}
|
||||
|
@ -195,53 +221,55 @@ export class CustomizationOptions {
|
|||
}
|
||||
}
|
||||
return current;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
public static getIdentity(community:string=null){
|
||||
let COLORS= {
|
||||
default:{
|
||||
mainColor:'#4687E6',
|
||||
|
||||
public static getIdentity(community: string = null) {
|
||||
let COLORS = {
|
||||
default: {
|
||||
mainColor: '#4687E6',
|
||||
secondaryColor: '#2D72D6'
|
||||
},
|
||||
"covid-19":{
|
||||
mainColor:"#03ADEE",
|
||||
"covid-19": {
|
||||
mainColor: "#03ADEE",
|
||||
secondaryColor: "#F15157"
|
||||
}
|
||||
};
|
||||
if(community && COLORS[community]){
|
||||
if (community && COLORS[community]) {
|
||||
return COLORS[community];
|
||||
}
|
||||
return COLORS.default;
|
||||
}
|
||||
public static getRGBA(color, A){
|
||||
if(color.indexOf("#")!=-1){
|
||||
return 'rgba('+parseInt(color.substring(1,3),16)+','+parseInt(color.substring(3,5),16)+','+parseInt(color.substring(5,7),16)+','+A+')';
|
||||
|
||||
public static getRGBA(color, A) {
|
||||
if (color.indexOf("#") != -1) {
|
||||
return 'rgba(' + parseInt(color.substring(1, 3), 16) + ',' + parseInt(color.substring(3, 5), 16) + ',' + parseInt(color.substring(5, 7), 16) + ',' + A + ')';
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
public static isForLightBackground(color:string){
|
||||
|
||||
public static isForLightBackground(color: string) {
|
||||
let L, r, g, b, a = 1;
|
||||
if(color.length == 7 || color.length == 9) {
|
||||
if (color.length == 7 || color.length == 9) {
|
||||
r = parseInt(color.substring(1, 3), 16);
|
||||
g = parseInt(color.substring(3, 5), 16);
|
||||
b = parseInt(color.substring(5, 7), 16);
|
||||
if(color.length == 9) {
|
||||
a = parseInt(color.substring(7, 9), 16);
|
||||
if (color.length == 9) {
|
||||
a = parseInt(color.substring(7, 9), 16);
|
||||
}
|
||||
}else if(color.length > 9){
|
||||
} else if (color.length > 9) {
|
||||
let array = color.split("rgba(")[1].split(")")[0].split(",");
|
||||
r = parseInt(array[0]);
|
||||
g = parseInt(array[1]);
|
||||
b = parseInt(array[2]);
|
||||
a = +array[3];
|
||||
|
||||
|
||||
}
|
||||
const brightness = r* 0.299 + g * 0.587 + b * 0.114 + (1 - a) * 255;
|
||||
|
||||
const brightness = r * 0.299 + g * 0.587 + b * 0.114 + (1 - a) * 255;
|
||||
|
||||
return (brightness < 186)
|
||||
|
||||
|
||||
// return !(r*0.299 + g*0.587 + b*0.114 > 186);
|
||||
/*for(let c of [r,g,b]){
|
||||
c = c / 255.0;
|
||||
|
@ -255,11 +283,12 @@ export class CustomizationOptions {
|
|||
return L > 0.179// (L + 0.05) / (0.05) > (1.0 + 0.05) / (L + 0.05); //use #000000 else use #ffffff
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
export class ButtonsCustomization{
|
||||
isDefault:boolean;
|
||||
|
||||
export class ButtonsCustomization {
|
||||
isDefault: boolean;
|
||||
backgroundColor: string;
|
||||
color: string;
|
||||
borderStyle: string;
|
||||
|
|
|
@ -11,9 +11,9 @@ import {ActivatedRoute} from "@angular/router";
|
|||
<li *ngIf="isPortalAdmin && !portal" [class.uk-active]="tab === 'portal'"><a routerLink="../portals">Portals</a></li>
|
||||
<li [class.uk-active]="tab === 'page'"><a routerLink="../pages">Pages</a></li>
|
||||
<li [class.uk-active]="tab === 'entity'"><a routerLink="../entities">Entities</a></li>
|
||||
<li *ngIf="portal && type === 'community'" [class.uk-active]="tab === 'menu'"><a routerLink="../menu">Menus</a></li>
|
||||
<li *ngIf="isPortalAdmin && !portal" [class.uk-active]="tab === 'class'"><a routerLink="../classes">Classes</a></li>
|
||||
<li *ngIf="isPortalAdmin && portal=='connect'" [class.uk-active]="tab === 'customization'"><a routerLink="../customization">Customization</a></li>
|
||||
<li *ngIf="portal && type === 'community'" [class.uk-active]="tab === 'menu'"><a routerLink="../menu">Menus</a></li>
|
||||
<li *ngIf="isPortalAdmin && !portal" [class.uk-active]="tab === 'class'"><a routerLink="../classes">Classes</a></li>
|
||||
<li *ngIf="isPortalAdmin && portal=='connect'" [class.uk-active]="tab === 'customization'"><a routerLink="../customization">Customization</a></li>
|
||||
</ul>
|
||||
`
|
||||
})
|
||||
|
@ -25,7 +25,7 @@ export class AdminTabsComponent implements OnInit {
|
|||
@Input()
|
||||
public user: User;
|
||||
@Input()
|
||||
public tab: "portal" | "page" | "entity" | "menu" | "class" | "customization"= 'page';
|
||||
public tab: "portal" | "page" | "entity" | "menu" | "class" | "customization" = 'page';
|
||||
private subscriptions: any[] = [];
|
||||
|
||||
constructor(private route: ActivatedRoute, private userManagementService: UserManagementService) {
|
||||
|
|
|
@ -95,13 +95,16 @@
|
|||
</div>
|
||||
</div>
|
||||
<modal-alert #inviteModal (alertOutput)="invite()" [overflowBody]="false" classTitle="uk-background-primary uk-light"
|
||||
[okDisabled]="invited && invited.invalid">
|
||||
[okDisabled]="!valid">
|
||||
<div *ngIf="message" class="uk-margin-medium-bottom uk-text-small">
|
||||
<div [innerHTML]="message | safeHtml"></div>
|
||||
</div>
|
||||
<div *ngIf="invited">
|
||||
<div input [formInput]="invited"
|
||||
placeholder="Email"></div>
|
||||
<div *ngIf="emailsForm">
|
||||
<div #emailInput input [formInput]="emailsForm" type="chips"
|
||||
placeholder="Recipients" hint="Add a recipient" [visibleChips]="3"
|
||||
[addExtraChips]="true" [validators]="validators" [separators]="[',']">
|
||||
<div note>Separate emails with commas</div>
|
||||
</div>
|
||||
</div>
|
||||
</modal-alert>
|
||||
<modal-alert #deleteModal [overflowBody]="false" (alertOutput)="deleteActive()" classTitle="uk-background-primary uk-light">
|
||||
|
|
|
@ -1,5 +1,21 @@
|
|||
import {Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild} from '@angular/core';
|
||||
import {UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms';
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
SimpleChanges,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import {
|
||||
UntypedFormArray,
|
||||
UntypedFormBuilder,
|
||||
UntypedFormControl,
|
||||
UntypedFormGroup,
|
||||
ValidatorFn,
|
||||
Validators
|
||||
} from '@angular/forms';
|
||||
import {AlertModal} from "../../../utils/modal/alert";
|
||||
import {UserRegistryService} from "../../../services/user-registry.service";
|
||||
import {EnvProperties} from "../../../utils/properties/env-properties";
|
||||
|
@ -9,9 +25,22 @@ import {UserManagementService} from "../../../services/user-management.service";
|
|||
import {Router} from "@angular/router";
|
||||
import {StringUtils} from "../../../utils/string-utils.class";
|
||||
import {NotificationService} from "../../../notifications/notification.service";
|
||||
import {Subscription} from "rxjs";
|
||||
import {forkJoin, of, Subscription} from "rxjs";
|
||||
import {NotificationHandler} from "../../../utils/notification-handler";
|
||||
import {ClearCacheService} from "../../../services/clear-cache.service";
|
||||
import {catchError, map, tap} from "rxjs/operators";
|
||||
import notification = CKEDITOR.plugins.notification;
|
||||
import {InputComponent} from "../../../sharedComponents/input/input.component";
|
||||
|
||||
class InvitationResponse {
|
||||
email: string;
|
||||
invitation: any;
|
||||
|
||||
constructor(email, invitation) {
|
||||
this.email = email;
|
||||
this.invitation = invitation;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'role-users',
|
||||
|
@ -49,7 +78,8 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
|
|||
public loadActive: boolean = true;
|
||||
public loadPending: boolean = true;
|
||||
public selectedUser: string = null;
|
||||
public invited: UntypedFormControl;
|
||||
public emailsForm: UntypedFormArray;
|
||||
public validators: ValidatorFn[] = [Validators.email];
|
||||
public properties: EnvProperties = properties;
|
||||
public exists: boolean = true;
|
||||
public roleFb: UntypedFormGroup;
|
||||
|
@ -60,6 +90,7 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
|
|||
/** Search */
|
||||
filterForm: UntypedFormGroup;
|
||||
@ViewChild('inviteModal') inviteModal: AlertModal;
|
||||
@ViewChild('emailInput') emailInput: InputComponent;
|
||||
@ViewChild('deleteModal') deleteModal: AlertModal;
|
||||
@ViewChild('deletePendingModal') deletePendingModal: AlertModal;
|
||||
@ViewChild('createRoleModal') createRoleModal: AlertModal;
|
||||
|
@ -69,6 +100,7 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
|
|||
private clearCacheService: ClearCacheService,
|
||||
private notificationService: NotificationService,
|
||||
private router: Router,
|
||||
private cdr: ChangeDetectorRef,
|
||||
private fb: UntypedFormBuilder) {
|
||||
}
|
||||
|
||||
|
@ -172,7 +204,8 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
|
|||
this.inviteModal.alertTitle = 'Invite ' + this.role;
|
||||
this.inviteModal.okButtonLeft = false;
|
||||
this.inviteModal.okButtonText = 'Send';
|
||||
this.invited = this.fb.control('', [Validators.required, Validators.email]);
|
||||
this.emailsForm = this.fb.array([], Validators.required);
|
||||
this.cdr.detectChanges();
|
||||
this.inviteModal.open();
|
||||
}
|
||||
|
||||
|
@ -224,33 +257,56 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
|
|||
this.loadPending = false;
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
get valid() {
|
||||
return this.emailsForm && this.emailsForm.valid || (this.emailInput && this.emailInput.searchControl.getRawValue() && this.emailInput.searchControl.valid);
|
||||
}
|
||||
|
||||
invite() {
|
||||
if(this.emailInput.searchControl.getRawValue() && this.emailInput.searchControl.valid) {
|
||||
this.emailInput.add(null,true);
|
||||
}
|
||||
this.showCurrent = false;
|
||||
this.loadPending = true;
|
||||
this.selectedUser = this.invited.value;
|
||||
let details = {
|
||||
link: this.link,
|
||||
email: this.emailComposer(this.name, this.invited.value, this.role)
|
||||
}
|
||||
this.subs.push(this.userRegistryService.invite(this.type, this.id, details, this.role).subscribe(invitation => {
|
||||
if (!this.pending.includes(this.invited.value)) {
|
||||
this.pending.push(this.invited.value);
|
||||
let current = null;
|
||||
let invitations = (<Array<any>>this.emailsForm.value).map(email => {
|
||||
current = email;
|
||||
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.subs.push(forkJoin(invitations).subscribe(responses => {
|
||||
let notifications = responses.map(response => {
|
||||
if(response.invitation) {
|
||||
NotificationHandler.rise(StringUtils.capitalize(this.role) + ' invitation to ' + response.email + ' has been <b>sent</b>');
|
||||
if (!this.pending.includes(response.email)) {
|
||||
this.pending.push(response.email);
|
||||
}
|
||||
if(this.notificationFn) {
|
||||
return this.notificationService.sendNotification(this.notificationFn(this.name, response.email, this.role, response.invitation)).pipe(map(notification => {
|
||||
return notification;
|
||||
}), tap(() => {
|
||||
NotificationHandler.rise('A notification has been <b>sent</b> successfully to ' + response.email);
|
||||
}), catchError( error => {
|
||||
NotificationHandler.rise('An error has occurred while sending a notification to ' + response.email + '. Please try again later', 'danger');
|
||||
return of(null);
|
||||
}));
|
||||
} else {
|
||||
return of(null);
|
||||
}
|
||||
} else {
|
||||
NotificationHandler.rise('An error has occurred while sending the invitation mail to ' + response.email + '.Please try again later', 'danger');
|
||||
return of(null);
|
||||
}
|
||||
});
|
||||
this.subs.push(forkJoin(notifications).subscribe(() => {
|
||||
this.pendingPage = Math.ceil(this.pending.length / this.pageSize);
|
||||
this.filterPendingBySearch(this.filterForm.value.pending);
|
||||
}
|
||||
if (this.notificationFn) {
|
||||
this.subs.push(this.notificationService.sendNotification(this.notificationFn(this.name, this.invited.value, this.role, invitation)).subscribe(notification => {
|
||||
NotificationHandler.rise('A notification has been <b>sent</b> successfully');
|
||||
}, error => {
|
||||
NotificationHandler.rise('An error has occurred. Please try again later', 'danger');
|
||||
}));
|
||||
}
|
||||
NotificationHandler.rise(StringUtils.capitalize(this.role) + ' invitation to ' + this.selectedUser + ' has been <b>sent</b>');
|
||||
this.loadPending = false;
|
||||
}, error => {
|
||||
NotificationHandler.rise('An error has occurred. Please try again later', 'danger');
|
||||
this.loadPending = false;
|
||||
this.loadPending = false;
|
||||
}))
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -3,15 +3,25 @@ import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from "@angular/com
|
|||
import {Observable, throwError, TimeoutError} from "rxjs";
|
||||
import {catchError} from "rxjs/operators";
|
||||
import {Session} from "./login/utils/helper.class";
|
||||
import {Router} from "@angular/router";
|
||||
import {GuardsCheckEnd, GuardsCheckStart, Router} from "@angular/router";
|
||||
import {LoginErrorCodes} from "./login/utils/guardHelper.class";
|
||||
import {properties} from "../../environments/environment";
|
||||
import * as url from "url";
|
||||
|
||||
@Injectable()
|
||||
export class ErrorInterceptorService implements HttpInterceptor {
|
||||
|
||||
private static UNAUTHORIZED_WHITELIST = [properties.userInfoUrl, properties.orcidAPIURL, properties.registryUrl? (properties.registryUrl + 'verification/'):null, properties.eoscDataTransferAPI].filter(value => !!value);
|
||||
private url: string = null;
|
||||
|
||||
constructor(private router: Router) {
|
||||
this.router.events.forEach(event => {
|
||||
if(event instanceof GuardsCheckStart) {
|
||||
this.url = event.url;
|
||||
} else if(event instanceof GuardsCheckEnd) {
|
||||
this.url = null;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
|
@ -42,7 +52,7 @@ export class ErrorInterceptorService implements HttpInterceptor {
|
|||
this.router.navigate(['/user-info'], {
|
||||
queryParams: {
|
||||
'errorCode': LoginErrorCodes.NOT_LOGIN,
|
||||
'redirectUrl': this.router.url
|
||||
'redirectUrl': this.url?this.url:this.router.url
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -51,7 +61,7 @@ export class ErrorInterceptorService implements HttpInterceptor {
|
|||
this.router.navigate(['/user-info'], {
|
||||
queryParams: {
|
||||
'errorCode': LoginErrorCodes.NOT_AUTHORIZED,
|
||||
'redirectUrl': this.router.url
|
||||
'redirectUrl': this.url?this.url:this.router.url
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -35,8 +35,11 @@ export class HttpInterceptorService implements HttpInterceptor {
|
|||
return of(response);
|
||||
} else {
|
||||
if (isPlatformServer(this.platformId)) {
|
||||
if(request.url.startsWith('/assets') || request.url.startsWith('assets')) {
|
||||
return of(null);
|
||||
}
|
||||
let headers = new HttpHeaders();
|
||||
if(request.withCredentials) {
|
||||
if(request.withCredentials && !properties.hasMachineCache) {
|
||||
headers = headers.set('Cookie', this.req.get('Cookie'));
|
||||
}
|
||||
const authReq = request.clone({
|
||||
|
|
|
@ -215,8 +215,8 @@ export class DataProviderComponent {
|
|||
this.stickyHeader = false;
|
||||
this.updateDescription("");
|
||||
|
||||
if(data["pv"]) {
|
||||
this.prevPath = data["pv"];
|
||||
if(data["return_path"]) {
|
||||
this.prevPath = data["return_path"] + (data["search_params"] ? ("?"+data["search_params"]) : "");
|
||||
}
|
||||
if((typeof document !== 'undefined') && document.referrer) {
|
||||
this.referrer = document.referrer;
|
||||
|
@ -984,7 +984,11 @@ export class DataProviderComponent {
|
|||
|
||||
public addEoscPrevInParams(obj) {
|
||||
if(properties.adminToolsPortalType == "eosc" && this.prevPath) {
|
||||
return this.routerHelper.addQueryParam("pv", this.prevPath, obj);
|
||||
let splitted: string[] = this.prevPath.split("?");
|
||||
obj = this.routerHelper.addQueryParam("return_path", splitted[0], obj);
|
||||
if(splitted.length > 0) {
|
||||
obj = this.routerHelper.addQueryParam("search_params", splitted[1], obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
|
|
@ -141,7 +141,11 @@ export class RelatedDatasourcesTabComponent {
|
|||
|
||||
public addEoscPrevInParams(obj) {
|
||||
if(properties.adminToolsPortalType == "eosc" && this.prevPath) {
|
||||
return this.routerHelper.addQueryParam("pv", this.prevPath, obj);
|
||||
let splitted: string[] = this.prevPath.split("?");
|
||||
obj = this.routerHelper.addQueryParam("return_path", splitted[0], obj);
|
||||
if(splitted.length > 0) {
|
||||
obj = this.routerHelper.addQueryParam("search_params", splitted[1], obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
|
|
@ -140,7 +140,11 @@ export class AvailableOnComponent {
|
|||
|
||||
public addEoscPrevInParams(obj) {
|
||||
if(properties.adminToolsPortalType == "eosc" && this.prevPath) {
|
||||
return this.routerHelper.addQueryParam("pv", this.prevPath, obj);
|
||||
let splitted: string[] = this.prevPath.split("?");
|
||||
obj = this.routerHelper.addQueryParam("return_path", splitted[0], obj);
|
||||
if(splitted.length > 0) {
|
||||
obj = this.routerHelper.addQueryParam("search_params", splitted[1], obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
|
|
@ -132,7 +132,11 @@ export class FundedByComponent {
|
|||
|
||||
public addEoscPrevInParams(obj) {
|
||||
if(properties.adminToolsPortalType == "eosc" && this.prevPath) {
|
||||
return this.routerHelper.addQueryParam("pv", this.prevPath, obj);
|
||||
let splitted: string[] = this.prevPath.split("?");
|
||||
obj = this.routerHelper.addQueryParam("return_path", splitted[0], obj);
|
||||
if(splitted.length > 0) {
|
||||
obj = this.routerHelper.addQueryParam("search_params", splitted[1], obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,9 @@ import {EnvProperties} from '../../../utils/properties/env-properties';
|
|||
import {ClickEvent} from "../../../utils/click/click-outside-or-esc.directive";
|
||||
import {NumberUtils} from "../../../utils/number-utils.class";
|
||||
import {OpenaireEntities} from "../../../utils/properties/searchFields";
|
||||
import {StringUtils} from "../../../utils/string-utils.class";
|
||||
import {properties} from "../../../../../environments/environment";
|
||||
import {RouterHelper} from "../../../utils/routerHelper.class";
|
||||
|
||||
@Component({
|
||||
selector: 'metrics',
|
||||
|
@ -77,7 +80,7 @@ import {OpenaireEntities} from "../../../utils/properties/searchFields";
|
|||
<tbody>
|
||||
<tr *ngFor="let key of getKeys(metrics.infos)">
|
||||
<td class="uk-width-1-3 uk-text-center uk-text-truncate" uk-tooltip [title]="metrics.infos.get(key).name">
|
||||
<a [href]="metrics.infos.get(key).url + (prevPath ? ('&pv='+prevPath) : '')">
|
||||
<a [queryParams]="addEoscPrevInParams({datasourceId: metrics.infos.get(key).url})" [routerLink]="properties.searchLinkToDataProvider.split('?')[0]">
|
||||
{{metrics.infos.get(key).name}}
|
||||
</a>
|
||||
</td>
|
||||
|
@ -151,6 +154,7 @@ export class MetricsComponent {
|
|||
public status: number;
|
||||
public state: number = -1;
|
||||
public openaireEntities = OpenaireEntities;
|
||||
public routerHelper:RouterHelper = new RouterHelper();
|
||||
|
||||
constructor(private metricsService: MetricsService, private cdr: ChangeDetectorRef) {
|
||||
}
|
||||
|
@ -313,4 +317,15 @@ export class MetricsComponent {
|
|||
let formatted = NumberUtils.roundNumber(+num);
|
||||
return formatted.number + formatted.size;
|
||||
}
|
||||
|
||||
public addEoscPrevInParams(obj) {
|
||||
if(properties.adminToolsPortalType == "eosc" && this.prevPath) {
|
||||
let splitted: string[] = this.prevPath.split("?");
|
||||
obj = this.routerHelper.addQueryParam("return_path", splitted[0], obj);
|
||||
if(splitted.length > 0) {
|
||||
obj = this.routerHelper.addQueryParam("search_params", splitted[1], obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,11 @@ import {MetricsService} from '../../../services/metrics.service';
|
|||
import {ErrorMessagesModule} from '../../../utils/errorMessages.module';
|
||||
import {IFrameModule} from "../../../utils/iframe.module";
|
||||
import {IconsModule} from "../../../utils/icons/icons.module";
|
||||
import {RouterModule} from "@angular/router";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule, FormsModule, ErrorMessagesModule, IFrameModule, IconsModule
|
||||
CommonModule, FormsModule, RouterModule, ErrorMessagesModule, IFrameModule, IconsModule
|
||||
],
|
||||
declarations: [
|
||||
MetricsComponent
|
||||
|
|
|
@ -8,29 +8,13 @@ import {StringUtils} from "../../utils/string-utils.class";
|
|||
providedIn: 'root'
|
||||
})
|
||||
export class ParsingFunctions {
|
||||
public eoscSubjects = [
|
||||
{
|
||||
label: 'EOSC::Jupyter Notebook',
|
||||
link: 'https://' + (properties.environment != 'production' ? 'beta.' : '') + 'marketplace.eosc-portal.eu/services?tag=EOSC%3A%3AJupyter+Notebook',
|
||||
value: 'Jupyter Notebook'
|
||||
},
|
||||
{
|
||||
label: 'EOSC::RO-crate',
|
||||
link: 'https://' + (properties.environment != 'production' ? 'beta.' : '') + 'marketplace.eosc-portal.eu/datasources/eosc.psnc.6f0470e3bb9203ec3a7553f3a72a7a1f?q=ROHub',
|
||||
value: 'RO-crate'
|
||||
},
|
||||
{
|
||||
label: 'EOSC::Galaxy Workflow',
|
||||
link: 'https://' + (properties.environment != 'production' ? 'beta.' : '') + 'marketplace.eosc-portal.eu/services?tag=EOSC%3A%3AGalaxy+Workflow',
|
||||
value: 'Galaxy Workflow'
|
||||
},
|
||||
{
|
||||
label: 'EOSC::Twitter Data',
|
||||
link: 'https://' + (properties.environment != 'production' ? 'beta.' : '') + 'marketplace.eosc-portal.eu/services?tag=EOSC%3A%3ATwitter+Data',
|
||||
value: 'Twitter Data'
|
||||
}
|
||||
]
|
||||
|
||||
public eoscSubjects = [
|
||||
{label: 'EOSC::Jupyter Notebook', link: 'https://' + (properties.environment != 'production'?'beta.':'') + 'search.marketplace.eosc-portal.eu/search/service?q=*&fq=tag_list:"EOSC%5C:%5C:Jupyter%20Notebook"', value: 'Jupyter Notebook'},
|
||||
{label: 'EOSC::RO-crate', link: 'https://' + (properties.environment != 'production'?'beta.':'') + 'search.marketplace.eosc-portal.eu/search/data-source?q=*&fq=tag_list:%22eosc%5C:%5C:ro%5C-crate%22', value: 'RO-crate'},
|
||||
{label: 'EOSC::Galaxy Workflow', link: 'https://' + (properties.environment != 'production'?'beta.':'') + 'search.marketplace.eosc-portal.eu/search/service?q=*&fq=tag_list:%22eosc%5C:%5C:galaxy%20workflow%22', value: 'Galaxy Workflow'},
|
||||
{label: 'EOSC::Twitter Data', link: 'https://' + (properties.environment != 'production'?'beta.':'') + 'search.marketplace.eosc-portal.eu/search/service?q=*&fq=tag_list:%22EOSC%5C:%5C:Twitter%20Data%22', value: 'Twitter Data'},
|
||||
{label: 'EOSC::Data Cube', link: 'https://' + (properties.environment != 'production'?'beta.':'') + 'search.marketplace.eosc-portal.eu/search/service?q=*&fq=tag_list:%22EOSC%5C:%5C:Data%20Cube%22', value: 'Data Cube'}
|
||||
]
|
||||
public notebookInSubjects: boolean = false;
|
||||
private notebookKeyword: string = "eosc jupyter notebook";
|
||||
private notebook_label: string = "EOSC";
|
||||
|
|
|
@ -184,8 +184,8 @@ export class OrganizationComponent {
|
|||
this.updateTitle("Organization");
|
||||
this.updateDescription("");
|
||||
|
||||
if(params["pv"]) {
|
||||
this.prevPath = params["pv"];
|
||||
if(params["return_path"]) {
|
||||
this.prevPath = params["return_path"] + (params["search_params"] ? ("?"+params["search_params"]) : "");
|
||||
}
|
||||
if((typeof document !== 'undefined') && document.referrer) {
|
||||
this.referrer = document.referrer;
|
||||
|
|
|
@ -241,8 +241,8 @@ export class ProjectComponent {
|
|||
this.updateTitle(title);
|
||||
this.updateDescription(description);
|
||||
|
||||
if(params["pv"]) {
|
||||
this.prevPath = params["pv"];
|
||||
if(params["return_path"]) {
|
||||
this.prevPath = params["return_path"] + (params["search_params"] ? ("?"+params["search_params"]) : "");
|
||||
}
|
||||
if((typeof document !== 'undefined') && document.referrer) {
|
||||
this.referrer = document.referrer;
|
||||
|
@ -1099,7 +1099,11 @@ export class ProjectComponent {
|
|||
|
||||
public addEoscPrevInParams(obj) {
|
||||
if(properties.adminToolsPortalType == "eosc" && this.prevPath) {
|
||||
return this.routerHelper.addQueryParam("pv", this.prevPath, obj);
|
||||
let splitted: string[] = this.prevPath.split("?");
|
||||
obj = this.routerHelper.addQueryParam("return_path", splitted[0], obj);
|
||||
if(splitted.length > 0) {
|
||||
obj = this.routerHelper.addQueryParam("search_params", splitted[1], obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
|
|
@ -68,8 +68,7 @@
|
|||
</orcid-work>
|
||||
</li>
|
||||
<li *ngIf=" properties.enableEoscDataTransfer && resultLandingInfo.resultType == 'dataset' &&
|
||||
resultLandingInfo.identifiers && resultLandingInfo.identifiers.get('doi') &&
|
||||
resultLandingInfo.identifiers.get('doi').join('').indexOf('zenodo.')!=-1"
|
||||
resultLandingInfo.identifiers && resultLandingInfo.identifiers.get('doi')"
|
||||
class="uk-text-center">
|
||||
<egi-transfer-data [dois]="resultLandingInfo.identifiers.get('doi')" [isOpen]="egiTransferModalOpen"></egi-transfer-data>
|
||||
</li>
|
||||
|
@ -120,7 +119,8 @@
|
|||
<div class="uk-grid uk-grid-small uk-flex-between uk-text-xsmall uk-flex-middle uk-grid-divider" uk-grid>
|
||||
<div class="uk-width-auto">
|
||||
<div class="uk-grid uk-grid-small uk-child-width-auto" uk-grid>
|
||||
<availableOn *ngIf="resultLandingInfo?.hostedBy_collectedFrom?.length" [availableOn]="resultLandingInfo.hostedBy_collectedFrom"></availableOn>
|
||||
<availableOn *ngIf="resultLandingInfo?.hostedBy_collectedFrom?.length"
|
||||
[availableOn]="resultLandingInfo.hostedBy_collectedFrom" [prevPath]="prevPath"></availableOn>
|
||||
<!-- Versions -->
|
||||
<a *ngIf="resultLandingInfo?.deletedByInferenceIds" (click)="openDeletedByInference()"
|
||||
class="uk-flex uk-flex-middle uk-flex-center uk-button-link uk-text-bolder">
|
||||
|
@ -596,7 +596,7 @@
|
|||
<div class="clickable uk-button-link uk-flex uk-flex-middle uk-h6 uk-margin-remove uk-padding-small uk-padding-remove-horizontal"
|
||||
(click)=" onSelectActiveTab('availableOn')">
|
||||
<availableOn [availableOn]="resultLandingInfo.hostedBy_collectedFrom" (viewAllClicked)="viewAll=$event"
|
||||
[isMobile]="true" [usedBy]="'landing'" class="uk-width-1-1"></availableOn>
|
||||
[isMobile]="true" [usedBy]="'landing'" class="uk-width-1-1" [prevPath]="prevPath"></availableOn>
|
||||
</div>
|
||||
<hr class="uk-margin-remove">
|
||||
</ng-container>
|
||||
|
@ -990,7 +990,8 @@
|
|||
<img src="assets/common-assets/eosc-logo.png"
|
||||
loading="lazy" alt="eosc_logo" style="width:27px; height:27px">
|
||||
<div class="uk-margin-small-left uk-flex uk-flex-column uk-flex-center">
|
||||
<div *ngFor="let subject of resultLandingInfo.eoscSubjects; let i=index" class="uk-text-truncate">
|
||||
<div *ngFor="let subject of resultLandingInfo.eoscSubjects; let i=index" class="uk-text-truncate"
|
||||
uk-tooltip="This interoperability link will provide you with a list of services able to process it in EOSC.">
|
||||
<a [href]="subject.link" target="_blank" class="custom-external">{{subject.value}}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -242,9 +242,8 @@ export class ResultLandingComponent {
|
|||
this.egiTransferModalOpen = true;
|
||||
}
|
||||
this.updateDescription("");
|
||||
|
||||
if(data["pv"]) {
|
||||
this.prevPath = data["pv"];
|
||||
if(data["return_path"]) {
|
||||
this.prevPath = data["return_path"] + (data["search_params"] ? ("?"+data["search_params"]) : "");
|
||||
}
|
||||
if((typeof document !== 'undefined') && document.referrer) {
|
||||
this.referrer = document.referrer;
|
||||
|
@ -772,7 +771,7 @@ export class ResultLandingComponent {
|
|||
}
|
||||
if(!this.identifier) {
|
||||
this._location.go(( pid ? (this.linkToLandingPage.split("?")[0] + "?pid=" + pid.id):
|
||||
(this.linkToLandingPage + this.id)) + (this.prevPath ? ("&pv="+this.prevPath) : ""));
|
||||
(this.linkToLandingPage + this.id)) + this.getEoscParams());
|
||||
}
|
||||
// else {
|
||||
// this._location.go(this.linkToLandingPage.split("?")[0] + "?pid=" + this.identifier.id);
|
||||
|
@ -1126,15 +1125,31 @@ export class ResultLandingComponent {
|
|||
}
|
||||
|
||||
public getAccessLabel(accessRight) : string {
|
||||
if(accessRight) {
|
||||
if (accessRight) {
|
||||
return (accessRight + (accessRight.toLowerCase().endsWith(" access") ? "" : " access"));
|
||||
}
|
||||
return "Not available access";
|
||||
}
|
||||
|
||||
public getEoscParams() {
|
||||
let params = "";
|
||||
if(this.prevPath) {
|
||||
let splitted: string[] = this.prevPath.split("?");
|
||||
params = "&return_path="+StringUtils.URIEncode(splitted[0]);
|
||||
if(splitted.length > 0) {
|
||||
params += "&search_params="+StringUtils.URIEncode(splitted[1]);
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
public addEoscPrevInParams(obj) {
|
||||
if(properties.adminToolsPortalType == "eosc" && this.prevPath) {
|
||||
return this.routerHelper.addQueryParam("pv", this.prevPath, obj);
|
||||
let splitted: string[] = this.prevPath.split("?");
|
||||
obj = this.routerHelper.addQueryParam("return_path", splitted[0], obj);
|
||||
if(splitted.length > 0) {
|
||||
obj = this.routerHelper.addQueryParam("search_params", splitted[1], obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,9 @@ import {LoginErrorCodes} from './utils/guardHelper.class';
|
|||
import {UserManagementService} from "../services/user-management.service";
|
||||
import {map, tap} from "rxjs/operators";
|
||||
|
||||
@Injectable()
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AdminLoginGuard implements CanActivate, CanActivateChild {
|
||||
|
||||
constructor(private router: Router,
|
||||
|
|
|
@ -6,15 +6,17 @@ import {
|
|||
CanLoad,
|
||||
Route,
|
||||
Router,
|
||||
RouterStateSnapshot, UrlTree
|
||||
RouterStateSnapshot,
|
||||
UrlTree
|
||||
} from '@angular/router';
|
||||
import {Observable} from 'rxjs';
|
||||
import {Session} from './utils/helper.class';
|
||||
import {LoginErrorCodes} from './utils/guardHelper.class';
|
||||
import {map, tap} from "rxjs/operators";
|
||||
import {UserManagementService} from "../services/user-management.service";
|
||||
|
||||
@Injectable()
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LoginGuard implements CanActivate, CanLoad, CanActivateChild {
|
||||
|
||||
constructor(private router: Router,
|
||||
|
|
|
@ -6,8 +6,44 @@ export class User {
|
|||
fullname: string;
|
||||
expirationDate: number;
|
||||
role: string[];
|
||||
jwt: string;
|
||||
|
||||
accessToken?: string;
|
||||
refreshToken?: string;
|
||||
|
||||
constructor(info: any) {
|
||||
this.id = (info.sub && info.sub.indexOf('@')) ? info.sub.substring(0, info.sub.indexOf('@')) : info.sub;
|
||||
this.firstname = (info.given_name) ? info.given_name : "";
|
||||
this.lastname = (info.family_name) ? info.family_name : "";
|
||||
this.email = info.email.toLowerCase(); // TODO remove, is a quick fix
|
||||
if(info.accessToken) {
|
||||
this.accessToken = info.accessToken;
|
||||
}
|
||||
if(info.refreshToken) {
|
||||
this.refreshToken = info.refreshToken;
|
||||
}
|
||||
this.fullname = (info.name) ? info.name : "";
|
||||
if (this.fullname == "") {
|
||||
if (this.firstname != "") {
|
||||
this.fullname += this.firstname;
|
||||
}
|
||||
if (this.lastname != "") {
|
||||
this.fullname += this.lastname;
|
||||
}
|
||||
if (this.fullname == "") { //fullname is still empty set a default
|
||||
this.fullname = "Anonymous user";
|
||||
}
|
||||
}
|
||||
this.role = [];
|
||||
if (info.roles) {
|
||||
info.roles.forEach(role => {
|
||||
this.role.push(role);
|
||||
});
|
||||
} else {
|
||||
if (info.edu_person_entitlements) {
|
||||
this.role = info.edu_person_entitlements;
|
||||
}
|
||||
}
|
||||
this.expirationDate = info.exp_date;
|
||||
}
|
||||
}
|
||||
|
||||
export class Session {
|
||||
|
|
|
@ -12,6 +12,7 @@ export type IndicatorType = 'number' | 'chart';
|
|||
export type IndicatorSize = 'small' | 'medium' | 'large';
|
||||
export type IndicatorPathType = 'table' | 'bar' | 'column' | 'pie' | 'line' | 'other';
|
||||
export type SourceType = 'statistics' | 'search' |'stats-tool' | 'old' | 'image';
|
||||
export type Format = 'NUMBER' | 'PERCENTAGE';
|
||||
export type Visibility = 'PUBLIC' | 'PRIVATE' | 'RESTRICTED';
|
||||
|
||||
export class Stakeholder {
|
||||
|
@ -21,6 +22,8 @@ export class Stakeholder {
|
|||
index_id;
|
||||
index_name: string;
|
||||
index_shortName: string;
|
||||
statsProfile: string = "monitor";
|
||||
locale: string = 'eu';
|
||||
alias: string;
|
||||
defaultId: string;
|
||||
visibility: Visibility;
|
||||
|
@ -192,8 +195,9 @@ export class IndicatorPath {
|
|||
parameters: any;
|
||||
filters: any;
|
||||
filtersApplied: number = 0;
|
||||
format: Format;
|
||||
|
||||
constructor(type: IndicatorPathType, source: SourceType, url: string, chartObject: string, jsonPath: string[]) {
|
||||
constructor(type: IndicatorPathType, source: SourceType, url: string, chartObject: string, jsonPath: string[], format: Format = 'NUMBER') {
|
||||
this.type = type;
|
||||
this.url = url;
|
||||
this.source = source;
|
||||
|
@ -202,6 +206,7 @@ export class IndicatorPath {
|
|||
this.parameters = {};
|
||||
this.filters = {};
|
||||
this.filtersApplied = 0;
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
static createParameters(funderName: string = null, title: string = null, chartType: string = null): any {
|
||||
|
@ -213,8 +218,10 @@ export class IndicatorPath {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export type FilterType = "fundingL0"|"start_year" | "end_year" | "co-funded";
|
||||
export class IndicatorFilterUtils{
|
||||
|
||||
export class IndicatorFilterUtils {
|
||||
|
||||
static getFilter(fieldPath: string, filterType:FilterType) {
|
||||
if((filterType == "start_year" || filterType == "end_year") && (fieldPath.indexOf(".year") != -1 || fieldPath.indexOf(".start year") != -1)
|
||||
|
|
|
@ -184,6 +184,8 @@ export class TerminologyComponent implements OnInit, OnDestroy, AfterViewInit, A
|
|||
this.subscriptions.forEach(subscription => {
|
||||
if (subscription instanceof Subscription) {
|
||||
subscription.unsubscribe();
|
||||
} else if(typeof ResizeObserver != 'undefined' && subscription instanceof ResizeObserver) {
|
||||
subscription.disconnect();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -26,27 +26,27 @@ export class ResourcesService {
|
|||
constructor(private http: HttpClient) {
|
||||
}
|
||||
|
||||
private async getResourcesItemsAsync(prefix = '', portal: string = null): Promise<MenuItem[]> {
|
||||
private async getResourcesItemsAsync(prefix = '', portal: string = null, target = '_self'): Promise<MenuItem[]> {
|
||||
let items = [
|
||||
new MenuItem("methodology", "Methodology", "", "", false, [],
|
||||
null, {}, null, null, null, null, '_self'),
|
||||
ResourcesService.setLink(new MenuItem("methodological-approach", "Methodological Approach",
|
||||
"", "", false, [], null, {}, null, null, null, null, '_self'),
|
||||
"", "", false, [], null, {}, null, null, null, null, target),
|
||||
prefix + "/methodology/methodological-approach", portal),
|
||||
ResourcesService.setLink(new MenuItem("terminology", "Terminology and construction",
|
||||
"", "", false, [], null, {}, null, null, null, null, '_self'),
|
||||
"", "", false, [], null, {}, null, null, null, null, target),
|
||||
prefix + "/methodology/terminology", portal)];
|
||||
items.push(new MenuItem("indicators-page", "Indicators",
|
||||
"", "", false, [], null, {}));
|
||||
items.push(ResourcesService.setLink(new MenuItem("indicator-themes", "Indicator Themes",
|
||||
"", "", false, [], null, {}, null, null, null, null, '_self'), prefix + "/indicators/themes", portal));
|
||||
"", "", false, [], null, {}, null, null, null, null, target), prefix + "/indicators/themes", portal));
|
||||
let promise = new Promise<void>(resolve => {
|
||||
this.isPagesEnabled().subscribe(status => {
|
||||
ResourcesService.types.forEach((type, index) => {
|
||||
if (status[index]) {
|
||||
items.push(ResourcesService.setLink(
|
||||
new MenuItem("indicators-" + type.value, type.label,
|
||||
"", "", false, [], null, {}, null, null, null, null, '_self'),
|
||||
"", "", false, [], null, {}, null, null, null, null, target),
|
||||
prefix + "/indicators/" + type.value, portal)
|
||||
);
|
||||
}
|
||||
|
@ -61,13 +61,13 @@ export class ResourcesService {
|
|||
return items;
|
||||
}
|
||||
|
||||
setResources(items: MenuItem[], prefix = '', portal: string = null) {
|
||||
setResources(items: MenuItem[], prefix = '', portal: string = null, target = '_self') {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
let resources = new MenuItem('resources', 'Resources', "", "", false, [], null, {});
|
||||
let index = items.push(resources) - 1;
|
||||
this.subscription = from(this.getResourcesItemsAsync(prefix, portal)).subscribe(resourcesItems => {
|
||||
this.subscription = from(this.getResourcesItemsAsync(prefix, portal, target)).subscribe(resourcesItems => {
|
||||
items[index].items = resourcesItems;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,13 +4,10 @@ import {BehaviorSubject, from, Observable, Subscriber} from "rxjs";
|
|||
import {Indicator, Section, Stakeholder, StakeholderInfo, Visibility} from "../entities/stakeholder";
|
||||
import {HelperFunctions} from "../../utils/HelperFunctions.class";
|
||||
import {map} from "rxjs/operators";
|
||||
import {ActivatedRoute} from "@angular/router";
|
||||
import {properties} from "../../../../environments/environment";
|
||||
import {CustomOptions} from "../../services/servicesUtils/customOptions.class";
|
||||
import {StringUtils} from "../../utils/string-utils.class";
|
||||
|
||||
let maps: string[] = ['parameters', 'filters'];
|
||||
|
||||
export interface Reorder {
|
||||
action: 'moved' | 'added' | 'removed',
|
||||
target: string,
|
||||
|
@ -21,25 +18,25 @@ export interface Reorder {
|
|||
providedIn: "root"
|
||||
})
|
||||
export class StakeholderService {
|
||||
|
||||
|
||||
private stakeholderSubject: BehaviorSubject<Stakeholder> = null;
|
||||
private promise: Promise<void>;
|
||||
private sub;
|
||||
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
this.stakeholderSubject = new BehaviorSubject<Stakeholder>(null);
|
||||
}
|
||||
|
||||
|
||||
ngOnDestroy() {
|
||||
this.clearSubscriptions();
|
||||
}
|
||||
|
||||
|
||||
clearSubscriptions() {
|
||||
if (this.sub instanceof Subscriber) {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getStakeholder(alias: string, shouldUpdate: boolean = false): Observable<Stakeholder> {
|
||||
if (!this.stakeholderSubject.value || this.stakeholderSubject.value.alias !== alias || shouldUpdate) {
|
||||
this.promise = new Promise<void>((resolve, reject) => {
|
||||
|
@ -56,78 +53,80 @@ export class StakeholderService {
|
|||
}
|
||||
return from(this.getStakeholderAsync());
|
||||
}
|
||||
|
||||
|
||||
async getStakeholderAsync() {
|
||||
if(this.promise) {
|
||||
if (this.promise) {
|
||||
await this.promise;
|
||||
this.promise = null;
|
||||
}
|
||||
this.clearSubscriptions();
|
||||
return this.stakeholderSubject.getValue();
|
||||
}
|
||||
|
||||
|
||||
getAlias(url: string): Observable<string[]> {
|
||||
return this.http.get<Stakeholder[]>(url + '/stakeholder/alias', CustomOptions.registryOptions()).pipe(map(stakeholders => {
|
||||
return this.formalize(stakeholders);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
getStakeholders(url: string, type: string = null, defaultId: string = null): Observable<(Stakeholder & StakeholderInfo)[]> {
|
||||
return this.http.get<Stakeholder[]>(url + '/stakeholder' + ((type) ? ('?type=' + type) : '') + ((!type && defaultId)? ('?defaultId=' + defaultId):''), CustomOptions.registryOptions()).pipe(map(stakeholders => {
|
||||
return this.http.get<Stakeholder[]>(url + '/stakeholder' + ((type) ? ('?type=' + type) : '') + ((!type && defaultId) ? ('?defaultId=' + defaultId) : ''), CustomOptions.registryOptions()).pipe(map(stakeholders => {
|
||||
return this.formalize(this.checkIsUpload(stakeholders));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
getMyStakeholders(url: string, type: string = null): Observable<(Stakeholder & StakeholderInfo)[]> {
|
||||
return this.http.get<Stakeholder[]>(url + '/my-stakeholder' + ((type) ? ('?type=' + type) : ''), CustomOptions.registryOptions()).pipe(map(stakeholders => {
|
||||
return this.formalize(this.checkIsUpload(stakeholders));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
getDefaultStakeholders(url: string, type: string = null): Observable<Stakeholder[]> {
|
||||
return this.http.get<Stakeholder[]>(url + '/stakeholder/default' + ((type) ? ('?type=' + type) : ''), CustomOptions.registryOptions()).pipe(map(stakeholders => {
|
||||
return this.formalize(this.checkIsUpload(stakeholders));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
buildStakeholder(url: string, stakeholder: Stakeholder): Observable<Stakeholder> {
|
||||
if(stakeholder.alias && stakeholder.alias.startsWith('/')) {
|
||||
stakeholder.alias = stakeholder.alias.slice(1);
|
||||
}
|
||||
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 => {
|
||||
return this.formalize(this.checkIsUpload(stakeholder));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
changeVisibility(url: string, path: string[], visibility: Visibility, propagate: boolean = false): Observable<any> {
|
||||
return this.http.post<Visibility>(url + '/' + path.join('/') + '/change-visibility' + '?visibility=' + visibility + (propagate?'&propagate=true':''), null, CustomOptions.registryOptions());
|
||||
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> {
|
||||
if(element.alias && element.alias.startsWith('/')) {
|
||||
element.alias = element.alias.slice(1);
|
||||
}
|
||||
if (element.alias && element.alias.startsWith('/')) {
|
||||
element.alias = element.alias.slice(1);
|
||||
}
|
||||
path = HelperFunctions.encodeArray(path);
|
||||
return this.http.post<any>(url + ((path.length > 0) ? '/' : '') + path.join('/') +
|
||||
'/save', element, CustomOptions.registryOptions()).pipe(map(element => {
|
||||
if(path.length === 0) {
|
||||
return this.formalize(this.checkIsUpload(element));
|
||||
} else {
|
||||
return this.formalize(element);
|
||||
}
|
||||
}));
|
||||
}
|
||||
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) {
|
||||
if (path.length === 0) {
|
||||
return this.formalize(this.checkIsUpload(element));
|
||||
} else {
|
||||
return this.formalize(element);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
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) {
|
||||
return this.formalize(this.checkIsUpload(element));
|
||||
} else {
|
||||
return this.formalize(element);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
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('/') +
|
||||
|
@ -135,7 +134,7 @@ export class StakeholderService {
|
|||
return this.formalize(element);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
deleteElement(url: string, path: string[], childrenAction: string = null): Observable<any> {
|
||||
path = HelperFunctions.encodeArray(path);
|
||||
let params: string = "";
|
||||
|
@ -144,29 +143,29 @@ export class StakeholderService {
|
|||
}
|
||||
return this.http.delete<any>(url + '/' + path.join('/') + '/delete' + params, CustomOptions.registryOptions());
|
||||
}
|
||||
|
||||
|
||||
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 => {
|
||||
return this.formalize(indicators);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
getStakeholderAsObservable(): Observable<Stakeholder> {
|
||||
return this.stakeholderSubject.asObservable();
|
||||
}
|
||||
|
||||
|
||||
setStakeholder(stakeholder: Stakeholder) {
|
||||
this.stakeholderSubject.next(stakeholder);
|
||||
}
|
||||
|
||||
|
||||
private checkIsUpload(response: Stakeholder | Stakeholder[]): any | any[] {
|
||||
if(Array.isArray(response)) {
|
||||
if (Array.isArray(response)) {
|
||||
response.forEach(value => {
|
||||
value.isUpload = value.logoUrl && !StringUtils.isValidUrl(value.logoUrl);
|
||||
});
|
||||
|
@ -175,7 +174,7 @@ export class StakeholderService {
|
|||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
private formalize(element: any) {
|
||||
return HelperFunctions.copy(element);
|
||||
}
|
||||
|
|
|
@ -55,26 +55,26 @@ export class SdgComponent implements OnInit, OnDestroy {
|
|||
this.updateUrl(this.url);
|
||||
this.updateTitle(this.pageTitle);
|
||||
this.updateDescription(this.pageDescription);
|
||||
this.vocabulariesService.getSDGs(properties).subscribe(data => {
|
||||
this.subscriptions.push(this.vocabulariesService.getSDGs(properties).subscribe(data => {
|
||||
this.sdgs = data['sdg'];
|
||||
});
|
||||
this.subscriptions.push(this.refineFieldResultsService.getRefineFieldsResultsByEntityName(['sdg'], 'result', this.properties, refineParams).subscribe(data => {
|
||||
this.sdgsResearchOutcomes = data[1][0].values;
|
||||
let merged =[];
|
||||
for(let i=0; i<this.sdgs.length; i++){
|
||||
merged.push({
|
||||
...this.sdgs[i],
|
||||
...(this.sdgsResearchOutcomes.find((innerItem) => innerItem.id === this.sdgs[i].id))
|
||||
});
|
||||
}
|
||||
this.displayedSdgs = merged;
|
||||
this.loading = false;
|
||||
}));
|
||||
}));
|
||||
let refineParams = null;
|
||||
if(this.customFilter) {
|
||||
let refineValue = StringUtils.URIEncode(this.customFilter.queryFieldName + " exact " + StringUtils.quote((this.customFilter.valueId)));
|
||||
refineParams = '&fq=' + refineValue;
|
||||
}
|
||||
this.refineFieldResultsService.getRefineFieldsResultsByEntityName(['sdg'], 'result', this.properties, refineParams).subscribe(data => {
|
||||
this.sdgsResearchOutcomes = data[1][0].values;
|
||||
let merged =[];
|
||||
for(let i=0; i<this.sdgs.length; i++){
|
||||
merged.push({
|
||||
...this.sdgs[i],
|
||||
...(this.sdgsResearchOutcomes.find((innerItem) => innerItem.id === this.sdgs[i].id))
|
||||
});
|
||||
}
|
||||
this.displayedSdgs = merged;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy() {
|
||||
|
|
|
@ -56,6 +56,7 @@ export class EntitiesSelectionComponent {
|
|||
showPage["" + data['pages'][i]["route"] + ""] = data['pages'][i]["isEnabled"];
|
||||
}
|
||||
}
|
||||
this.entities = [];
|
||||
if(this.onlyresults) {
|
||||
if(this.simpleView) {
|
||||
this.entities.push({label: 'All ' + OpenaireEntities.RESULTS.toLowerCase(), value: 'all'});
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<errorMessages [status]="[status]" [type]="'results'"></errorMessages>
|
||||
<li *ngFor="let result of results">
|
||||
<div class="uk-card uk-card-default uk-position-relative uk-flex uk-flex-column uk-flex-center"
|
||||
[ngClass]="result.type" [class.uk-disabled]="!hasPermission(result)">
|
||||
[ngClass]="result.type" [class.noColor]="properties.adminToolsPortalType == 'connect'" [class.uk-disabled]="!hasPermission(result)">
|
||||
<div *ngIf="type === 'community' && result.isSubscribed" [class.uk-position-top-left]="!isMobile" [class.uk-position-top-right]="isMobile" class="uk-text-background uk-text-center uk-padding-small uk-text-uppercase uk-text-bold">
|
||||
<span>Member</span>
|
||||
</div>
|
||||
|
@ -49,7 +49,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div *ngIf="result.description && !isMobile" class="uk-margin-top uk-text-small multi-line-ellipsis lines-3">
|
||||
<p class="uk-text-meta" [innerHTML]="result.description"></p>
|
||||
<p class="uk-text-meta" >{{result.description | htmlToString}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="uk-width-1-5@m uk-width-1-3 uk-flex-first@m">
|
||||
|
|
|
@ -11,15 +11,15 @@
|
|||
|
||||
@media(min-width: @breakpoint-medium) {
|
||||
.uk-card {
|
||||
&.funder {
|
||||
&.funder:not(.noColor) {
|
||||
.setType(@funder-color);
|
||||
}
|
||||
|
||||
&.ri {
|
||||
&.ri:not(.noColor) {
|
||||
.setType(@ri-color);
|
||||
}
|
||||
|
||||
&.organization {
|
||||
&.organization:not(.noColor) {
|
||||
.setType(@organization-color);
|
||||
}
|
||||
}
|
||||
|
@ -27,15 +27,15 @@
|
|||
|
||||
@media(max-width: @breakpoint-small-max) {
|
||||
.uk-card {
|
||||
&.funder {
|
||||
&.funder:not(.noColor) {
|
||||
.setType(@funder-color, bottom);
|
||||
}
|
||||
|
||||
&.ri {
|
||||
&.ri:not(.noColor) {
|
||||
.setType(@ri-color, bottom);
|
||||
}
|
||||
|
||||
&.organization {
|
||||
&.organization:not(.noColor) {
|
||||
.setType(@organization-color, bottom);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,13 @@ import {UrlPrefixModule} from "../../utils/pipes/url-prefix.module";
|
|||
import {IconsService} from "../../utils/icons/icons.service";
|
||||
import {incognito, restricted} from "../../utils/icons/icons";
|
||||
import {LogoUrlPipeModule} from "../../utils/pipes/logoUrlPipe.module";
|
||||
import {HTMLToStringPipeModule} from "../../utils/pipes/HTMLToStringPipe.module";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule, FormsModule,
|
||||
RouterModule, ErrorMessagesModule,
|
||||
AlertModalModule, ManageModule, IconsModule, UrlPrefixModule, LogoUrlPipeModule
|
||||
AlertModalModule, ManageModule, IconsModule, UrlPrefixModule, LogoUrlPipeModule, HTMLToStringPipeModule
|
||||
],
|
||||
declarations: [
|
||||
PortalSearchResultComponent
|
||||
|
|
|
@ -58,7 +58,7 @@ export class MetricsService {
|
|||
info = {};
|
||||
|
||||
info.name = result[2];
|
||||
info.url = properties.searchLinkToDataProvider+id;
|
||||
info.url = id;
|
||||
info.numOfDownloads = "0";
|
||||
info.openaireDownloads = "0";
|
||||
info.numOfViews = "0";
|
||||
|
|
|
@ -41,7 +41,7 @@ export class UserManagementService {
|
|||
|
||||
public updateUserInfo(resolve: Function = null) {
|
||||
this.subscription = this.http.get<User>(properties.userInfoUrl, CustomOptions.registryOptions()).pipe(map(userInfo => {
|
||||
return this.parseUserInfo(userInfo);
|
||||
return new User(userInfo);
|
||||
})).subscribe(user => {
|
||||
this.getUserInfoSubject.next(user);
|
||||
if (resolve) {
|
||||
|
@ -54,39 +54,7 @@ export class UserManagementService {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
private parseUserInfo(info: any) {
|
||||
const user: User = new User();
|
||||
user.id = (info.sub && info.sub.indexOf('@')) ? info.sub.substring(0, info.sub.indexOf('@')) : info.sub;
|
||||
user.firstname = (info.given_name) ? info.given_name : "";
|
||||
user.lastname = (info.family_name) ? info.family_name : "";
|
||||
user.email = info.email.toLowerCase(); // TODO remove, is a quick fix
|
||||
user.fullname = (info.name) ? info.name : "";
|
||||
if (user.fullname == "") {
|
||||
if (user.firstname != "") {
|
||||
user.fullname += user.firstname;
|
||||
}
|
||||
if (user.lastname != "") {
|
||||
user.fullname += user.lastname;
|
||||
}
|
||||
if (user.fullname == "") { //fullname is still empty set a default
|
||||
user.fullname = "Anonymous user";
|
||||
}
|
||||
}
|
||||
user.role = [];
|
||||
if (info.roles) {
|
||||
info.roles.forEach(role => {
|
||||
user.role.push(role);
|
||||
});
|
||||
} else {
|
||||
if (info.edu_person_entitlements) {
|
||||
user.role = info.edu_person_entitlements;
|
||||
}
|
||||
}
|
||||
user.expirationDate = info.exp_date;
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
public setRedirectUrl(url: string = null) {
|
||||
if (url) {
|
||||
let parts = url.split('?');
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
<li><a target="_blank" href="https://www.openaire.eu/guides">Guides</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/faqs">FAQs</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/frontpage/webinars">Webinars</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/support/helpdesk">Ask a question</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/helpdesk">Ask a question</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -23,15 +23,15 @@ import {properties} from "../../../../environments/environment";
|
|||
import {ClickEvent} from "../../utils/click/click-outside-or-esc.directive";
|
||||
|
||||
export type InputType =
|
||||
'text'
|
||||
| 'URL'
|
||||
| 'logoURL'
|
||||
| 'autocomplete'
|
||||
| 'autocomplete_soft'
|
||||
| 'textarea'
|
||||
| 'select'
|
||||
| 'chips'
|
||||
| 'year-range';
|
||||
'text'
|
||||
| 'URL'
|
||||
| 'logoURL'
|
||||
| 'autocomplete'
|
||||
| 'autocomplete_soft'
|
||||
| 'textarea'
|
||||
| 'select'
|
||||
| 'chips'
|
||||
| 'year-range';
|
||||
|
||||
export interface Option {
|
||||
icon?: string,
|
||||
|
@ -70,144 +70,144 @@ declare var UIkit;
|
|||
@Component({
|
||||
selector: '[dashboard-input], [input]',
|
||||
template: `
|
||||
<div *ngIf="formControl" [id]="id">
|
||||
<div class="input-wrapper" [class.disabled]="formControl.disabled" [class.opened]="opened"
|
||||
[class.focused]="focused" [ngClass]="inputClass" [class.hint]="hint"
|
||||
[class.active]="!focused && (formAsControl?.value || selectable || formAsArray?.length > 0 || getLabel(formAsControl?.value) || yearRangeActive)"
|
||||
[class.danger]="(formControl.invalid && (formControl.touched || !!searchControl?.touched)) || (!!searchControl?.invalid && !!searchControl?.touched)">
|
||||
<div #inputBox class="input-box" [class.select]="selectable" click-outside-or-esc
|
||||
[class.static]="placeholderInfo?.static" (clickOutside)="click($event)">
|
||||
<div *ngIf="!placeholderInfo?.static && placeholderInfo?.label" class="placeholder">
|
||||
<label>{{placeholderInfo.label}} <sup *ngIf="required">*</sup></label>
|
||||
</div>
|
||||
<div class="uk-flex" [class.uk-flex-middle]="type !== 'textarea'"
|
||||
[attr.uk-tooltip]="(tooltip && !focused && type !== 'chips' && type !== 'textarea' && (formControl.value || hint || placeholderInfo?.label))?('title: ' + (formControl.value ?getTooltip(formControl.value):(hint?hint:placeholderInfo?.label)) + '; delay: 500; pos: bottom-left'):null">
|
||||
<ng-template [ngIf]="type === 'text' || type === 'URL' || type === 'logoURL'">
|
||||
<input #input class="input" [attr.placeholder]="placeholderInfo?.static?placeholderInfo.label:hint"
|
||||
[type]="password?'password':'text'" [formControl]="formAsControl"
|
||||
[class.uk-text-truncate]="!focused">
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="type === 'textarea'">
|
||||
<div *ngIf="formControl" [id]="id">
|
||||
<div class="input-wrapper" [class.disabled]="formControl.disabled" [class.opened]="opened"
|
||||
[class.focused]="focused" [ngClass]="inputClass" [class.hint]="hint"
|
||||
[class.active]="!focused && (formAsControl?.value || selectable || formAsArray?.length > 0 || getLabel(formAsControl?.value) || yearRangeActive)"
|
||||
[class.danger]="(formControl.invalid && (formControl.touched || !!searchControl?.touched)) || (!!searchControl?.invalid && !!searchControl?.touched)">
|
||||
<div #inputBox class="input-box" [class.select]="selectable" click-outside-or-esc
|
||||
[class.static]="placeholderInfo?.static" (clickOutside)="click($event)">
|
||||
<div *ngIf="!placeholderInfo?.static && placeholderInfo?.label" class="placeholder">
|
||||
<label>{{placeholderInfo.label}} <sup *ngIf="required">*</sup></label>
|
||||
</div>
|
||||
<div class="uk-flex" [class.uk-flex-middle]="type !== 'textarea'"
|
||||
[attr.uk-tooltip]="(tooltip && !focused && type !== 'chips' && type !== 'textarea' && (formControl.value || hint || placeholderInfo?.label))?('title: ' + (formControl.value ?getTooltip(formControl.value):(hint?hint:placeholderInfo?.label)) + '; delay: 500; pos: bottom-left'):null">
|
||||
<ng-template [ngIf]="type === 'text' || type === 'URL' || type === 'logoURL'">
|
||||
<input #input class="input" [attr.placeholder]="placeholderInfo?.static?placeholderInfo.label:hint"
|
||||
[type]="password?'password':'text'" [formControl]="formAsControl"
|
||||
[class.uk-text-truncate]="!focused">
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="type === 'textarea'">
|
||||
<textarea #textArea class="input" [attr.placeholder]="placeholderInfo?.static?placeholderInfo.label:hint"
|
||||
[rows]="rows" [formControl]="formAsControl"></textarea>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="type === 'select'">
|
||||
<ng-container *ngIf="placeholderInfo?.static">
|
||||
<div *ngIf="!getLabel(formControl.value)"
|
||||
class="input placeholder uk-width-expand uk-text-truncate">{{placeholderInfo.label}}</div>
|
||||
<div *ngIf="getLabel(formControl.value)"
|
||||
class="input uk-width-expand uk-text-truncate">{{getLabel(formControl.value)}}</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!placeholderInfo?.static">
|
||||
<div *ngIf="!getLabel(formControl.value)"
|
||||
class="input uk-width-expand uk-text-truncate">{{noValueSelected}}</div>
|
||||
<div *ngIf="getLabel(formControl.value)"
|
||||
class="input uk-width-expand uk-text-truncate">{{getLabel(formControl.value)}}</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="type === 'autocomplete'">
|
||||
<input *ngIf="focused" [attr.placeholder]="placeholderInfo?.static?placeholderInfo.label:hint"
|
||||
#searchInput class="input" [formControl]="searchControl" [class.uk-text-truncate]="!focused">
|
||||
<ng-container *ngIf="!focused && !selectable">
|
||||
<div *ngIf="!getLabel(formControl.value)"
|
||||
class="input placeholder uk-text-truncate">{{placeholderInfo?.static ? placeholderInfo.label : getLabel(formAsControl.value)}}</div>
|
||||
<div *ngIf="getLabel(formControl.value)"
|
||||
class="input uk-text-truncate">{{getLabel(formAsControl.value)}}</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!focused && selectable">
|
||||
<div *ngIf="!getLabel(formControl.value)" class="input uk-text-truncate">{{noValueSelected}}</div>
|
||||
<div *ngIf="getLabel(formControl.value)"
|
||||
class="input uk-text-truncate">{{getLabel(formControl.value)}}</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="type === 'autocomplete_soft'">
|
||||
<input #input class="input" [attr.placeholder]="placeholderInfo?.static?placeholderInfo.label:hint"
|
||||
[formControl]="formAsControl" [class.uk-text-truncate]="!focused">
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="type === 'chips'">
|
||||
<div class="uk-grid uk-grid-small uk-grid-row-collapse uk-overflow-auto uk-width-expand"
|
||||
[class.uk-flex-nowrap]="noWrap" [class.uk-overflow-auto]="noWrap" uk-grid>
|
||||
<div *ngFor="let chip of formAsArray.controls; let i=index" #chip
|
||||
[class.uk-hidden]="!focused && i > visibleChips - 1"
|
||||
class="chip">
|
||||
<div class="uk-label uk-label-small uk-text-transform-none uk-flex uk-flex-middle"
|
||||
[attr.uk-tooltip]="(tooltip)?('title: ' + getLabel(chip.value) + '; delay: 500; pos: bottom-left'):null">
|
||||
<span class="uk-text-truncate uk-width-expand">{{getLabel(chip.value)}}</span>
|
||||
<icon *ngIf="focused" (click)="remove(i, $event)"
|
||||
class="uk-link-text uk-margin-small-left clickable" [flex]="true"
|
||||
name="close" ratio="0.7"></icon>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="type === 'select'">
|
||||
<ng-container *ngIf="placeholderInfo?.static">
|
||||
<div *ngIf="!getLabel(formControl.value)"
|
||||
class="input placeholder uk-width-expand uk-text-truncate" [class.uk-disabled]="formControl.disabled">{{placeholderInfo.label}}</div>
|
||||
<div *ngIf="getLabel(formControl.value)"
|
||||
class="input uk-width-expand uk-text-truncate" [class.uk-disabled]="formControl.disabled">{{getLabel(formControl.value)}}</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!placeholderInfo?.static">
|
||||
<div *ngIf="!getLabel(formControl.value)"
|
||||
class="input uk-width-expand uk-text-truncate" [class.uk-disabled]="formControl.disabled">{{noValueSelected}}</div>
|
||||
<div *ngIf="getLabel(formControl.value)"
|
||||
class="input uk-width-expand uk-text-truncate" [class.uk-disabled]="formControl.disabled">{{getLabel(formControl.value)}}</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="type === 'autocomplete'">
|
||||
<input *ngIf="focused" [attr.placeholder]="placeholderInfo?.static?placeholderInfo.label:hint"
|
||||
#searchInput class="input" [formControl]="searchControl" [class.uk-text-truncate]="!focused">
|
||||
<ng-container *ngIf="!focused && !selectable">
|
||||
<div *ngIf="!getLabel(formControl.value)"
|
||||
class="input placeholder uk-text-truncate" [class.uk-disabled]="formControl.disabled">{{placeholderInfo?.static ? placeholderInfo.label : getLabel(formAsControl.value)}}</div>
|
||||
<div *ngIf="getLabel(formControl.value)"
|
||||
class="input uk-text-truncate" [class.uk-disabled]="formControl.disabled">{{getLabel(formAsControl.value)}}</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!focused && selectable">
|
||||
<div *ngIf="!getLabel(formControl.value)" class="input uk-text-truncate" [class.uk-disabled]="formControl.disabled">{{noValueSelected}}</div>
|
||||
<div *ngIf="getLabel(formControl.value)"
|
||||
class="input uk-text-truncate" [class.uk-disabled]="formControl.disabled">{{getLabel(formControl.value)}}</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="type === 'autocomplete_soft'">
|
||||
<input #input class="input" [attr.placeholder]="placeholderInfo?.static?placeholderInfo.label:hint"
|
||||
[formControl]="formAsControl" [class.uk-text-truncate]="!focused">
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="type === 'chips'">
|
||||
<div class="uk-grid uk-grid-small uk-grid-row-collapse uk-overflow-auto uk-width-expand"
|
||||
[class.uk-flex-nowrap]="noWrap" [class.uk-overflow-auto]="noWrap" uk-grid>
|
||||
<div *ngFor="let chip of formAsArray.controls; let i=index" #chip
|
||||
[class.uk-hidden]="!focused && i > visibleChips - 1"
|
||||
class="chip">
|
||||
<div class="uk-label uk-label-small uk-text-transform-none uk-flex uk-flex-middle"
|
||||
[attr.uk-tooltip]="(tooltip)?('title: ' + getLabel(chip.value) + '; delay: 500; pos: bottom-left'):null">
|
||||
<span class="uk-text-truncate uk-width-expand">{{getLabel(chip.value)}}</span>
|
||||
<icon *ngIf="focused" (click)="remove(i, $event)"
|
||||
class="uk-link-text uk-margin-small-left clickable" [flex]="true"
|
||||
name="close" ratio="0.7"></icon>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="searchControl && (focused || formAsArray.length === 0)" #chip
|
||||
class="uk-width-expand search-input uk-flex uk-flex-column uk-flex-center">
|
||||
<input #searchInput class="input" [class.search]="searchControl.value"
|
||||
[attr.placeholder]="placeholderInfo?.static?placeholderInfo.label:hint"
|
||||
[formControl]="searchControl" [class.uk-text-truncate]="!focused">
|
||||
</div>
|
||||
<div *ngIf="!focused && formAsArray.length > visibleChips"
|
||||
class="uk-width-expand uk-flex uk-flex-column uk-flex-center more">
|
||||
+ {{(formAsArray.length - visibleChips)}} more
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="type === 'year-range' && yearRange && formAsGroup">
|
||||
<div class="uk-width-2-5">
|
||||
<input #input class="input uk-text-center uk-text-truncate" [attr.placeholder]="yearRange.from.placeholder"
|
||||
maxlength="4" (click)="activeIndex = 0;$event.preventDefault()" [formControl]="getFormByName(yearRange.from.control)">
|
||||
</div>
|
||||
<div class="uk-width-1-5 uk-text-center">-</div>
|
||||
<div class="uk-width-2-5">
|
||||
<input #input class="input uk-text-center uk-text-truncate" [attr.placeholder]="yearRange.to.placeholder"
|
||||
maxlength="4" (click)="activeIndex = 1;$event.preventDefault()" [formControl]="getFormByName(yearRange.to.control)">
|
||||
</div>
|
||||
</ng-template>
|
||||
<div
|
||||
*ngIf="(formControl.disabled && disabledIcon) || icon || (selectable && selectArrow) || type === 'autocomplete' || searchable"
|
||||
class="uk-margin-small-left icon">
|
||||
<icon *ngIf="formControl.disabled && disabledIcon" [name]="disabledIcon" [flex]="true"></icon>
|
||||
<ng-template [ngIf]="formControl.enabled">
|
||||
<icon *ngIf="!searchControl?.value && icon" [name]="icon" [flex]="true"></icon>
|
||||
<icon *ngIf="!icon && selectable && selectArrow" [name]="selectArrow" [flex]="true"></icon>
|
||||
<button *ngIf="focused && type === 'autocomplete'" class="uk-close uk-icon"
|
||||
(click)="resetSearch($event)">
|
||||
<icon [flex]="true" name="close"></icon>
|
||||
</button>
|
||||
<button *ngIf="(!focused && type === 'autocomplete') || (type !== 'autocomplete' && !searchControl?.value && !!formControl?.value && (searchable || !selectable))"
|
||||
class="uk-close uk-icon" (click)="resetValue($event)">
|
||||
<icon [flex]="true" name="close"></icon>
|
||||
</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
<!-- use action-icon class in order to apply css in your icon button-->
|
||||
<ng-content select="[action]"></ng-content>
|
||||
</div>
|
||||
<div class="tools">
|
||||
<ng-content select="[tools]"></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="searchControl && (focused || formAsArray.length === 0)" #chip
|
||||
class="uk-width-expand search-input uk-flex uk-flex-column uk-flex-center">
|
||||
<input #searchInput class="input" [class.search]="searchControl.value"
|
||||
[attr.placeholder]="placeholderInfo?.static?placeholderInfo.label:hint"
|
||||
[formControl]="searchControl" [class.uk-text-truncate]="!focused">
|
||||
</div>
|
||||
<div *ngIf="!focused && formAsArray.length > visibleChips"
|
||||
class="uk-width-expand uk-flex uk-flex-column uk-flex-center more">
|
||||
+ {{(formAsArray.length - visibleChips)}} more
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="type === 'year-range' && yearRange && formAsGroup">
|
||||
<div class="uk-width-2-5">
|
||||
<input #input class="input uk-text-center uk-text-truncate" [attr.placeholder]="yearRange.from.placeholder"
|
||||
maxlength="4" (click)="activeIndex = 0;$event.preventDefault()" [formControl]="getFormByName(yearRange.from.control)">
|
||||
</div>
|
||||
<div class="uk-width-1-5 uk-text-center">-</div>
|
||||
<div class="uk-width-2-5">
|
||||
<input #input class="input uk-text-center uk-text-truncate" [attr.placeholder]="yearRange.to.placeholder"
|
||||
maxlength="4" (click)="activeIndex = 1;$event.preventDefault()" [formControl]="getFormByName(yearRange.to.control)">
|
||||
</div>
|
||||
</ng-template>
|
||||
<div
|
||||
*ngIf="(formControl.disabled && disabledIcon) || icon || (selectable && selectArrow) || type === 'autocomplete' || searchable"
|
||||
class="uk-margin-small-left icon">
|
||||
<icon *ngIf="formControl.disabled && disabledIcon" [name]="disabledIcon" [flex]="true"></icon>
|
||||
<ng-template [ngIf]="formControl.enabled">
|
||||
<icon *ngIf="!searchControl?.value && icon" [name]="icon" [flex]="true"></icon>
|
||||
<icon *ngIf="!icon && selectable && selectArrow" [name]="selectArrow" [flex]="true"></icon>
|
||||
<button *ngIf="!!searchControl?.value && type === 'autocomplete'" class="uk-close uk-icon"
|
||||
(click)="resetSearch($event)">
|
||||
<icon [flex]="true" name="close"></icon>
|
||||
</button>
|
||||
<button *ngIf="!searchControl?.value && !!formControl?.value && (searchable || !selectable)"
|
||||
class="uk-close uk-icon" (click)="resetValue($event)">
|
||||
<icon [flex]="true" name="close"></icon>
|
||||
</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
<!-- use action-icon class in order to apply css in your icon button-->
|
||||
<ng-content select="[action]"></ng-content>
|
||||
</div>
|
||||
<div class="tools">
|
||||
<ng-content select="[tools]"></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="options uk-dropdown" *ngIf="filteredOptions && filteredOptions.length > 0 && opened" #optionBox
|
||||
uk-dropdown="pos: bottom-justify; mode: none; offset: 15; boundary-align: true;" [attr.boundary]="'#' + id">
|
||||
<ul class="uk-nav uk-dropdown-nav">
|
||||
<li *ngFor="let option of filteredOptions; let i=index" [class.uk-hidden]="option.hidden"
|
||||
[class.uk-active]="(formControl.value === option.value) || selectedIndex === i">
|
||||
<a (click)="selectOption(option, $event)"
|
||||
[class]="option.disabled ? 'uk-disabled uk-text-muted' : ''">
|
||||
<div class="options uk-dropdown" *ngIf="filteredOptions && filteredOptions.length > 0 && opened" #optionBox
|
||||
uk-dropdown="pos: bottom-justify; mode: none; offset: 15; boundary-align: true;" [attr.boundary]="'#' + id">
|
||||
<ul class="uk-nav uk-dropdown-nav">
|
||||
<li *ngFor="let option of filteredOptions; let i=index" [class.uk-hidden]="option.hidden"
|
||||
[class.uk-active]="(formControl.value === option.value) || selectedIndex === i">
|
||||
<a (click)="selectOption(option, $event)"
|
||||
[class]="option.disabled ? 'uk-disabled uk-text-muted' : ''">
|
||||
<span
|
||||
[attr.uk-tooltip]="(tooltip)?('title: ' + (option.tooltip ? option.tooltip : option.label) + '; delay: 500; pos:bottom-left'):null">{{option.label}}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
[attr.uk-tooltip]="(tooltip)?('title: ' + (option.tooltip ? option.tooltip : option.label) + '; delay: 500; pos:bottom-left'):null">{{option.label}}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span *ngIf="formControl?.invalid && formControl?.touched" class="uk-text-danger">
|
||||
<span *ngIf="formControl?.invalid && formControl?.touched" class="uk-text-danger">
|
||||
<span *ngIf="errors?.error">{{errors?.error}}</span>
|
||||
<span *ngIf="type === 'URL' || type === 'logoURL'">Please provide a valid URL (e.g. https://example.com)</span>
|
||||
</span>
|
||||
<span class="uk-text-small uk-text-danger">
|
||||
<span class="uk-text-small uk-text-danger">
|
||||
<ng-content select="[error]"></ng-content>
|
||||
</span>
|
||||
<span *ngIf="formControl?.valid" class="uk-text-small uk-text-warning uk-margin-xsmall-top">
|
||||
<span *ngIf="formControl?.valid" class="uk-text-small uk-text-warning uk-margin-xsmall-top">
|
||||
<ng-content select="[warning]"></ng-content>
|
||||
<span *ngIf="!secure">
|
||||
<span class="uk-text-bold">Note:</span> Prefer urls like "<span class="uk-text-bold">https://</span>example.com/my-secure-image.png"
|
||||
|
@ -215,9 +215,9 @@ declare var UIkit;
|
|||
<span class="uk-text-bold">Browsers may not load non secure content.</span>
|
||||
</span>
|
||||
</span>
|
||||
<i class="uk-text-small uk-text-meta uk-margin-xsmall-top">
|
||||
<ng-content select="[note]"></ng-content>
|
||||
</i>
|
||||
<i class="uk-text-small uk-text-meta uk-margin-xsmall-top">
|
||||
<ng-content select="[note]"></ng-content>
|
||||
</i>
|
||||
`
|
||||
})
|
||||
export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChanges {
|
||||
|
@ -261,7 +261,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
/** Year Range Configuration */
|
||||
@Input() yearRange: YearRange;
|
||||
public activeIndex: 0 | 1 | null = null;
|
||||
|
||||
|
||||
@Input() visibleRows: number = -1;
|
||||
@Input() extendEnter: () => void = null;
|
||||
@Output() focusEmitter: EventEmitter<boolean> = new EventEmitter<boolean>();
|
||||
|
@ -282,7 +282,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
@ViewChild('optionBox') optionBox: ElementRef;
|
||||
@ViewChild('searchInput') searchInput: ElementRef;
|
||||
@ViewChildren('chip') chips: QueryList<ElementRef>;
|
||||
|
||||
|
||||
@Input()
|
||||
set placeholder(placeholder: string | Placeholder) {
|
||||
if(this.type === 'year-range') {
|
||||
|
@ -297,7 +297,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
this.placeholderInfo = placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Input()
|
||||
set options(options: (Option | string | number) []) {
|
||||
this.optionsArray = options.map(option => {
|
||||
|
@ -322,13 +322,13 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
this.selectable = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
constructor(private elementRef: ElementRef, private cdr: ChangeDetectorRef) {
|
||||
if (elementRef.nativeElement.hasAttribute('dashboard-input') && this.properties.environment === "development") {
|
||||
console.warn("'dashboard-input' selector is deprecated; use 'input' instead.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@HostListener('window:keydown.arrowUp', ['$event'])
|
||||
arrowUp(event: KeyboardEvent) {
|
||||
if (this.opened) {
|
||||
|
@ -339,7 +339,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@HostListener('window:keydown.arrowDown', ['$event'])
|
||||
arrowDown(event: KeyboardEvent) {
|
||||
if (this.opened) {
|
||||
|
@ -350,7 +350,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@HostListener('window:keydown.arrowLeft', ['$event'])
|
||||
arrowLeft(event: KeyboardEvent) {
|
||||
if (this.type === 'chips' && this.focused) {
|
||||
|
@ -364,7 +364,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@HostListener('window:keydown.arrowRight', ['$event'])
|
||||
arrowRight(event: KeyboardEvent) {
|
||||
if (this.type === 'chips' && this.focused) {
|
||||
|
@ -378,7 +378,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@HostListener('window:keydown.enter', ['$event'])
|
||||
enter(event: KeyboardEvent) {
|
||||
if (this.extendEnter) {
|
||||
|
@ -395,7 +395,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
this.focus(false, event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@HostListener('keydown', ['$event'])
|
||||
onKeyDown(event: KeyboardEvent) {
|
||||
if (this.separators.includes(event.key) || this.separators.includes(event.key.toLowerCase())) {
|
||||
|
@ -403,11 +403,11 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
this.add(event, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
click(event: ClickEvent) {
|
||||
this.focus(!event.clicked, event);
|
||||
this.focus(!event.clicked);
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
InputComponent.INPUT_COUNTER++;
|
||||
this.id = 'input-' + InputComponent.INPUT_COUNTER;
|
||||
|
@ -430,11 +430,11 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (this.formControl) {
|
||||
if (changes.value) {
|
||||
|
@ -455,11 +455,11 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.unsubscribe();
|
||||
}
|
||||
|
||||
|
||||
getFormByName(name: string): UntypedFormControl {
|
||||
if (this.formControl instanceof UntypedFormGroup) {
|
||||
return <UntypedFormControl>this.formControl.get(name);
|
||||
|
@ -467,7 +467,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get formAsGroup(): UntypedFormGroup {
|
||||
if (this.formControl instanceof UntypedFormGroup) {
|
||||
return this.formControl;
|
||||
|
@ -475,7 +475,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get formAsControl(): UntypedFormControl {
|
||||
if (this.formControl instanceof UntypedFormControl) {
|
||||
return this.formControl;
|
||||
|
@ -483,7 +483,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get formAsArray(): UntypedFormArray {
|
||||
if (this.formControl instanceof UntypedFormArray) {
|
||||
return this.formControl;
|
||||
|
@ -491,26 +491,26 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get yearRangeActive(): boolean {
|
||||
if(this.yearRange) {
|
||||
return this.formAsGroup && (this.getFormByName(this.yearRange.from.control)?.value || this.getFormByName(this.yearRange.to.control)?.value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
get errors(): any {
|
||||
if(this.formAsGroup) {
|
||||
return (this.formAsGroup.errors
|
||||
?this.formAsGroup.errors:(this.getFormByName(this.yearRange.from.control).errors
|
||||
?this.getFormByName(this.yearRange.from.control).errors:this.getFormByName(this.yearRange.to.control).errors));
|
||||
?this.formAsGroup.errors:(this.getFormByName(this.yearRange.from.control).errors
|
||||
?this.getFormByName(this.yearRange.from.control).errors:this.getFormByName(this.yearRange.to.control).errors));
|
||||
} else if(this.formAsControl) {
|
||||
return this.formAsControl.errors;
|
||||
} else {
|
||||
return this.searchControl.errors;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
reset() {
|
||||
this.secure = true;
|
||||
this.unsubscribe();
|
||||
|
@ -532,8 +532,10 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
if (this.focused) {
|
||||
this.open(true);
|
||||
setTimeout(() => {
|
||||
this.searchInput.nativeElement.focus();
|
||||
this.searchInput.nativeElement.value = value;
|
||||
if(this.searchInput) {
|
||||
this.searchInput.nativeElement.focus();
|
||||
this.searchInput.nativeElement.value = value;
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}));
|
||||
|
@ -599,7 +601,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsubscribe() {
|
||||
this.subscriptions.forEach(subscription => {
|
||||
if (subscription instanceof Subscription) {
|
||||
|
@ -607,7 +609,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
updateValidators() {
|
||||
if (this.formAsArray) {
|
||||
this.formAsArray.controls.forEach(control => {
|
||||
|
@ -619,7 +621,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
this.formControl.updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
remove(index: number, event) {
|
||||
if (this.focused) {
|
||||
this.formAsArray.removeAt(index);
|
||||
|
@ -629,7 +631,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private filter(value: string): Option[] {
|
||||
let options = this.optionsArray.filter(option => !option.hidden);
|
||||
if (this.type === "chips") {
|
||||
|
@ -647,7 +649,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
|
||||
add(event, addChips = false) {
|
||||
if (addChips && this.searchControl.value) {
|
||||
this.splitSearchControl();
|
||||
|
@ -655,7 +657,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
this.searchControl.setValue('');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
splitSearchControl() {
|
||||
let values = [this.searchControl.value];
|
||||
this.separators.forEach(separator => {
|
||||
|
@ -679,17 +681,17 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
this.searchControl.setValue('');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getLabel(value: any): string {
|
||||
let option = this.optionsArray.find(option => HelperFunctions.equals(option.value, value));
|
||||
return (option) ? option.label : (value);
|
||||
}
|
||||
|
||||
|
||||
getTooltip(value: any): string {
|
||||
let option = this.optionsArray.find(option => HelperFunctions.equals(option.value, value));
|
||||
return (option) ? (option.tooltip ? option.tooltip : option.label) : (value);
|
||||
}
|
||||
|
||||
|
||||
focus(value: boolean, event = null) {
|
||||
if(!this.activeIndex) {
|
||||
this.activeIndex = 0;
|
||||
|
@ -697,41 +699,43 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
if (this.focused) {
|
||||
this.formControl.markAsTouched();
|
||||
}
|
||||
this.focused = value;
|
||||
this.cdr.detectChanges();
|
||||
if (this.focused) {
|
||||
if (this.input?.length > 0) {
|
||||
this.input.get(this.activeIndex).nativeElement.focus();
|
||||
} else if (this.textArea) {
|
||||
this.textArea.nativeElement.focus();
|
||||
} else if (this.searchInput) {
|
||||
this.searchInput.nativeElement.focus();
|
||||
this.activeElement.next(this.chips.last);
|
||||
}
|
||||
if (this.selectArrow) {
|
||||
this.open(!this.opened);
|
||||
} else if (this.type !== 'autocomplete' || this.showOptionsOnEmpty || !this.formControl.value) {
|
||||
this.open(true);
|
||||
}
|
||||
} else {
|
||||
this.activeIndex = null;
|
||||
this.open(false);
|
||||
if (this.input) {
|
||||
this.input.forEach(input => {
|
||||
input.nativeElement.blur();
|
||||
})
|
||||
} else if (this.textArea) {
|
||||
this.textArea.nativeElement.blur();
|
||||
} else if (this.searchInput) {
|
||||
this.searchInput.nativeElement.blur();
|
||||
}
|
||||
if (this.searchControl) {
|
||||
this.add(event, this.addExtraChips);
|
||||
if(this.formControl.enabled) {
|
||||
this.focused = value;
|
||||
this.cdr.detectChanges();
|
||||
if (this.focused) {
|
||||
if (this.input?.length > 0) {
|
||||
this.input.get(this.activeIndex).nativeElement.focus();
|
||||
} else if (this.textArea) {
|
||||
this.textArea.nativeElement.focus();
|
||||
} else if (this.searchInput) {
|
||||
this.searchInput.nativeElement.focus();
|
||||
this.activeElement.next(this.chips.last);
|
||||
}
|
||||
if (this.selectArrow) {
|
||||
this.open(!this.opened);
|
||||
} else if (this.type !== 'autocomplete' || this.showOptionsOnEmpty || !this.formControl.value) {
|
||||
this.open(true);
|
||||
}
|
||||
} else {
|
||||
this.activeIndex = null;
|
||||
this.open(false);
|
||||
if (this.input) {
|
||||
this.input.forEach(input => {
|
||||
input.nativeElement.blur();
|
||||
})
|
||||
} else if (this.textArea) {
|
||||
this.textArea.nativeElement.blur();
|
||||
} else if (this.searchInput) {
|
||||
this.searchInput.nativeElement.blur();
|
||||
}
|
||||
if (this.searchControl) {
|
||||
this.add(event, this.addExtraChips);
|
||||
}
|
||||
}
|
||||
this.focusEmitter.emit(this.focused);
|
||||
}
|
||||
this.focusEmitter.emit(this.focused);
|
||||
}
|
||||
|
||||
|
||||
open(value: boolean) {
|
||||
this.opened = value && this.formControl.enabled;
|
||||
this.cdr.detectChanges();
|
||||
|
@ -748,19 +752,19 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resetSearch(event: any) {
|
||||
event.stopPropagation();
|
||||
this.searchControl.setValue('');
|
||||
this.focus(true, event);
|
||||
}
|
||||
|
||||
|
||||
resetValue(event: any) {
|
||||
event.stopPropagation();
|
||||
this.formControl.setValue('');
|
||||
this.focus(true, event);
|
||||
}
|
||||
|
||||
|
||||
selectOption(option: Option, event) {
|
||||
if (this.formControl.enabled) {
|
||||
if (this.formAsControl) {
|
||||
|
|
|
@ -333,7 +333,7 @@
|
|||
</div>
|
||||
<ng-template #header_template let-mobile="mobile">
|
||||
<a *ngIf="!activeHeader.url" [routerLink]="activeHeader.route" [class.uk-padding-remove]="!isHeaderLeft"
|
||||
class="uk-logo uk-navbar-item uk-flex uk-flex-middle">
|
||||
class="uk-logo uk-navbar-item uk-flex uk-flex-middle" [class.small]="activeHeader.logoInfo">
|
||||
<img *ngIf="(mobile && activeHeader.logoSmallUrl) || (!mobile && activeHeader.logoUrl)"
|
||||
[src]="!mobile?activeHeader.logoUrl:activeHeader.logoSmallUrl"
|
||||
[alt]="activeHeader.title">
|
||||
|
@ -345,7 +345,7 @@
|
|||
</ng-container>
|
||||
</a>
|
||||
<a *ngIf="activeHeader.url" [href]="activeHeader.url" [class.uk-padding-remove]="!isHeaderLeft"
|
||||
class="uk-logo uk-navbar-item uk-flex uk-flex-middle">
|
||||
class="uk-logo uk-navbar-item uk-flex uk-flex-middle" [class.small]="activeHeader.logoInfo">
|
||||
<img *ngIf="(mobile && activeHeader.logoSmallUrl) || (!mobile && activeHeader.logoUrl)"
|
||||
[src]="!mobile?activeHeader.logoUrl:activeHeader.logoSmallUrl"
|
||||
[alt]="activeHeader.title">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Component, Input, OnDestroy, OnInit} from "@angular/core";
|
||||
import {Component, Input, OnDestroy, OnInit, ViewChild} from "@angular/core";
|
||||
import {AbstractControl, FormBuilder, FormGroup, ValidationErrors, ValidatorFn, Validators} from "@angular/forms";
|
||||
import {Subscriber} from "rxjs";
|
||||
import {StringUtils} from "../../utils/string-utils.class";
|
||||
|
@ -11,13 +11,14 @@ import {EmailService} from "../../utils/email/email.service";
|
|||
import {properties} from "../../../../environments/environment";
|
||||
import {CommunityInfo} from "../../connect/community/communityInfo";
|
||||
import {NotificationHandler} from "../../utils/notification-handler";
|
||||
import {InputComponent} from "../input/input.component";
|
||||
|
||||
@Component({
|
||||
selector: 'subscriber-invite',
|
||||
template: `
|
||||
<div *ngIf="body" class="uk-grid uk-child-width-1-1" uk-grid [formGroup]="inviteForm">
|
||||
<div input [formInput]="inviteForm.get('name')" placeholder="From"></div>
|
||||
<div input [formInput]="inviteForm.get('recipients')" type="chips"
|
||||
<div #emailInput input [formInput]="inviteForm.get('recipients')" type="chips"
|
||||
placeholder="Recipients" hint="Add a recipient" [visibleChips]="3"
|
||||
[addExtraChips]="true" [validators]="validators" [separators]="[',']">
|
||||
<div note>Separate emails with commas</div>
|
||||
|
@ -61,7 +62,8 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
|
|||
public validators: ValidatorFn[] = [Validators.email];
|
||||
public loading: boolean = true;
|
||||
private subscriptions: any[] = [];
|
||||
|
||||
@ViewChild('emailInput') emailInput: InputComponent;
|
||||
|
||||
constructor(private fb: FormBuilder,
|
||||
private emailService: EmailService,
|
||||
private communityService: CommunityService) {
|
||||
|
@ -105,6 +107,9 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
invite() {
|
||||
if(this.emailInput.searchControl.getRawValue() && this.emailInput.searchControl.valid) {
|
||||
this.emailInput.add(null,true);
|
||||
}
|
||||
this.loading = true;
|
||||
this.body.paragraphs = this.inviteForm.getRawValue().message;
|
||||
this.email.body = Composer.formatEmailBodyForInvitation(this.body);
|
||||
|
@ -129,6 +134,6 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
get valid() {
|
||||
return !this.loading && this.inviteForm && this.inviteForm.valid;
|
||||
return !this.loading && this.inviteForm && this.inviteForm.valid || (this.emailInput && this.emailInput.searchControl.getRawValue() && this.emailInput.searchControl.valid);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Inject, Injectable, InjectionToken, PLATFORM_ID} from '@angular/core';
|
||||
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { timeout } from 'rxjs/operators';
|
||||
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
|
||||
import {Observable} from 'rxjs';
|
||||
import {timeout} from 'rxjs/operators';
|
||||
import {isPlatformServer} from "@angular/common";
|
||||
import {properties} from "../../environments/environment";
|
||||
|
||||
|
@ -9,30 +9,32 @@ export const DEFAULT_TIMEOUT = new InjectionToken<number>('defaultTimeout');
|
|||
|
||||
@Injectable()
|
||||
export class TimeoutInterceptor implements HttpInterceptor {
|
||||
// timeout inside services for: properties.searchCrossrefAPIURL, properties.searchDataciteAPIURL
|
||||
private static TIMEOUT_WHITELIST = [properties.csvAPIURL, properties.registryUrl, properties.claimsAPIURL,
|
||||
properties.searchCrossrefAPIURL, properties.searchDataciteAPIURL];
|
||||
|
||||
constructor(@Inject(DEFAULT_TIMEOUT) protected defaultTimeout: number, @Inject(PLATFORM_ID) private platformId: any) {
|
||||
}
|
||||
private static TIMEOUT_WHITELIST = [
|
||||
properties.csvAPIURL, properties.registryUrl, properties.claimsAPIURL,
|
||||
properties.searchCrossrefAPIURL, properties.searchDataciteAPIURL,
|
||||
properties.statisticsAPIURL, properties.searchAPIURLLAst, properties.monitorStatsFrameUrl];
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
if (req.method !== 'GET' || this.isService(req, TimeoutInterceptor.TIMEOUT_WHITELIST)) {
|
||||
return next.handle(req);
|
||||
constructor(@Inject(DEFAULT_TIMEOUT) protected defaultTimeout: number, @Inject(PLATFORM_ID) private platformId: any) {
|
||||
}
|
||||
|
||||
let serverTime = properties.environment == "production" ? 3000 : 4000;
|
||||
let clientTime = properties.environment == "production" ? 6000 : 12000;
|
||||
const timeoutValue = isPlatformServer(this.platformId)?serverTime:clientTime;//req.headers.get('timeout') || this.defaultTimeout;
|
||||
const timeoutValueNumeric = Number(timeoutValue);
|
||||
return next.handle(req).pipe(timeout(timeoutValueNumeric));
|
||||
}
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
if (req.method !== 'GET' || this.isService(req, TimeoutInterceptor.TIMEOUT_WHITELIST)) {
|
||||
return next.handle(req);
|
||||
}
|
||||
|
||||
isService(req: HttpRequest<any>, service: string | string[]):boolean {
|
||||
if(Array.isArray(service)) {
|
||||
return !!service.find(element => req.url.indexOf(element) !== -1);
|
||||
} else {
|
||||
return req.url.indexOf(service) !== -1;
|
||||
let serverTime = properties.environment == "production" ? 3000 : 4000;
|
||||
let clientTime = properties.environment == "production" ? 6000 : 12000;
|
||||
const timeoutValue = isPlatformServer(this.platformId) ? serverTime : clientTime;//req.headers.get('timeout') || this.defaultTimeout;
|
||||
const timeoutValueNumeric = Number(timeoutValue);
|
||||
return next.handle(req).pipe(timeout(timeoutValueNumeric));
|
||||
}
|
||||
|
||||
isService(req: HttpRequest<any>, service: string | string[]): boolean {
|
||||
if (Array.isArray(service)) {
|
||||
return !!service.find(element => req.url.indexOf(element) !== -1);
|
||||
} else {
|
||||
return req.url.indexOf(service) !== -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<a (click)="open()"
|
||||
[title]="'Send data to cloud storage [demo]'"
|
||||
[title]="'Send data to cloud storage'"
|
||||
[attr.uk-tooltip]="'pos: bottom; cls: uk-active uk-text-small uk-padding-small'"
|
||||
class="uk-flex uk-flex-middle uk-flex-center uk-button-link uk-text-bolder">
|
||||
<icon flex="true" ratio="0.8" name="cloud_upload" visuallyHidden="upload"></icon>
|
||||
|
@ -8,11 +8,11 @@
|
|||
|
||||
|
||||
<!-- This is the modal -->
|
||||
<modal-alert #egiTransferModal large="true" [okDisabled]="destinationPath.length == 0 || status == 'success'
|
||||
||status == 'loading' || !validatePath() || (!this.downloadElements || this.downloadElements.length == 0)"
|
||||
(alertOutput)="transfer()" >
|
||||
<fs-modal #egiTransferModal classBody="uk-container-xlarge" [okButtonDisabled]="destinationPath.length == 0 || status == 'succeeded'
|
||||
|| (requests > 0) || !validatePath() || !validateDestinationUrl() || (!this.downloadElements || this.downloadElements.length == 0)"
|
||||
(okEmitter)="transfer()" (cancelEmitter)="init()">
|
||||
<div *ngIf="!accessToken" class="">
|
||||
<div class="uk-width-1-1 uk-margin-top uk-margin-bottom uk-text-center">
|
||||
<div class="uk-width-1-1 uk-margin-bottom uk-text-center">
|
||||
In order to send data to a Cloud Storage, you would need to be authenticated, please login via EGI check-in.
|
||||
</div>
|
||||
<div class="uk-text-center">
|
||||
|
@ -21,18 +21,18 @@
|
|||
</div>
|
||||
</div>
|
||||
<div *ngIf="accessToken" class="">
|
||||
<div class="uk-width-1-1 uk-margin-top uk-margin-bottom uk-text-center">
|
||||
<div class="uk-width-1-1 uk-margin-bottom uk-text-center">
|
||||
You have requested to send the data corresponding to the DOI <a [href]="selectedSourceUrl" target="_blank">{{selectedSourceUrl.split(doiPrefix)[1]}}</a> to a cloud storage using the EOSC Data Transfer service
|
||||
</div>
|
||||
<div class="uk-grid uk-child-width-1-2 uk-grid-divider">
|
||||
<div class="uk-grid uk-grid-small uk-child-width-1-2 uk-grid-divider">
|
||||
<!-- Source -->
|
||||
<div class="uk-first-column source">
|
||||
<p class="uk-text-meta uk-text-xsmall uk-margin-remove-bottom uk-margin-top">Available Zenodo DOI URLs:</p>
|
||||
<div input type="select" [(value)]="selectedSourceUrl" placeholder="Zenodo DOI URL" hint="Select..."
|
||||
<p class="uk-text-meta uk-text-xsmall uk-margin-remove-bottom uk-margin-top">Available DOI URLs:</p>
|
||||
<div input type="select" [(value)]="selectedSourceUrl" hint="Select..." [inputClass]="'flat'"
|
||||
[options]="sourceUrls" (valueChange)="this.parse()"></div>
|
||||
<div *ngIf="status!='loading'" class="uk-margin-top">
|
||||
<div *ngIf="status!='active'" class="uk-margin-top">
|
||||
<div>{{this.downloadElements.length}} files found:</div>
|
||||
<div class="uk-overflow-auto" style="max-height: 135px">
|
||||
<div class="uk-margin-small-top uk-height-max-medium uk-overflow-auto">
|
||||
<ul>
|
||||
<li *ngFor=" let element of this.downloadElements">{{ element.name}}
|
||||
</li>
|
||||
|
@ -42,53 +42,100 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- Destination -->
|
||||
<div class="destination">
|
||||
<p class="uk-text-meta uk-text-xsmall uk-margin-remove-bottom uk-margin-top">Please select the Destination Storage type:</p>
|
||||
<div input type="select" [(value)]="selectedDestination" placeholder="Destination Storage" hint="Select..."
|
||||
[options]="destinationOptions"></div>
|
||||
<ng-container *ngIf="selectedDestination.id == 'dcache'">
|
||||
<p class="uk-text-meta uk-text-xsmall uk-margin-remove-bottom uk-margin-top">Provide the corresponding storage destination path:</p>
|
||||
<div input [(value)]="destinationPath" placeholder="Give a destination path..."
|
||||
[validators]="validators" class=""></div>
|
||||
<div *ngIf="selectedDestination.hasBrowse">
|
||||
<p class="uk-text-meta uk-text-xsmall uk-margin-remove-bottom"> or <a
|
||||
class="uk-text-primary" (click)="browseFolder(selectedDestination.defaultFolder)">browse</a> to
|
||||
select a folder.</p>
|
||||
<div *ngIf="folders[selectedDestination.defaultFolder]" class="uk-height-small uk-overflow-auto">
|
||||
<ng-container *ngTemplateOutlet="folderListTmpl; context: { folder : folders[selectedDestination.defaultFolder], folderPath: selectedDestination.defaultFolder}"></ng-container>
|
||||
<div *ngIf="destinationOptions" class="destination">
|
||||
<!-- -->
|
||||
<!-- Testing:<br>-->
|
||||
<!-- https://dcache-demo.desy.de:2443-->
|
||||
<!-- <br>-->
|
||||
<!-- /Demonstrators/EOSC-Future/EGI/-->
|
||||
<!-- <br>-->
|
||||
<!-- -->
|
||||
<p class="uk-text-meta uk-text-xsmall uk-margin-remove-bottom uk-margin-top">Destination storage type:</p>
|
||||
<div input type="select" [(value)]="selectedDestination" hint="Select..." [inputClass]="'flat'"
|
||||
[options]="destinationOptions" (valueChange)="folders = {}"></div>
|
||||
|
||||
<p class="uk-text-meta uk-text-xsmall uk-margin-remove-bottom uk-margin-top">Destination system (e.g. hostname:8080):</p>
|
||||
<div input [(value)]="destinationUrl" [inputClass]="'flat'"
|
||||
[validators]="URLValidators" class=""></div>
|
||||
|
||||
<ng-container
|
||||
*ngIf="selectedDestination.authType == 'password' || selectedDestination.authType == 'keys'">
|
||||
<p class="uk-text-meta uk-text-xsmall uk-margin-remove-bottom uk-margin-top">Authentication:</p>
|
||||
<div class="uk-grid uk-child-width-1-2">
|
||||
<div input [(value)]="destinationAuthUser" [placeholder]="'Give ' + (selectedDestination.authType ==
|
||||
'password'? 'username':'access key') " [inputClass]="'flat x-small'"
|
||||
class=""></div>
|
||||
<div input password=true [(value)]="destinationAuthPass" [placeholder]="'Give ' + (selectedDestination.authType ==
|
||||
'password'? 'password':'secret key') " [inputClass]="'flat x-small'"
|
||||
class=""></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedDestination.id == 'ftp'">
|
||||
<p>Comming soon!</p>
|
||||
<!-- <div class="uk-text-xsmall">You can check our data protection policy <a class="custom-external" href="https://www.openaire.eu/data-protection-policy" target="_blank">here</a>.</div>-->
|
||||
</ng-container>
|
||||
|
||||
<p class="uk-text-meta uk-text-xsmall uk-margin-remove-bottom uk-margin-top">Destination path (e.g. /folder1/folder2):</p>
|
||||
<div input [(value)]="destinationPath" [inputClass]="'flat'"
|
||||
[validators]="pathValidators" class=""></div>
|
||||
<!-- <div *ngIf="selectedDestination.hasBrowse">-->
|
||||
<p *ngIf="selectedDestination.canBrowse" class="uk-text-meta uk-text-xsmall uk-margin-remove-bottom"> or <a
|
||||
[ngClass]="!validateDestinationUrl() ? 'uk-text-muted uk-disabled' : 'uk-text-primary'" [attr.disabled]="!destinationUrl"
|
||||
(click)="browseFolder('/')">browse</a> to
|
||||
select a folder.</p>
|
||||
<div *ngIf="folders['/']" class="uk-height-max-medium uk-overflow-auto">
|
||||
<ng-container *ngTemplateOutlet="folderListTmpl; context: { folder : folders['/'], folderPath: '/'}"></ng-container>
|
||||
</div>
|
||||
|
||||
<!-- </div>-->
|
||||
<ng-container *ngIf="selectedDestination.destination == 'ftp'">
|
||||
<p>Comming soon!</p>
|
||||
</ng-container>
|
||||
|
||||
<div class="uk-margin-top">
|
||||
<!-- (ngModelChange)="filterChange(value.selected)"-->
|
||||
<input type="checkbox" class="uk-checkbox"
|
||||
[(ngModel)]="privacyAccepted"/>
|
||||
<span class="uk-margin-small-left uk-text-small">*I have read and accepted the data protection policy <a class="custom-external" href="https://www.openaire.eu/data-protection-policy" target="_blank">here</a>.</span>
|
||||
</div>
|
||||
|
||||
<div class="uk-align-right uk-margin-medium-top uk-margin-bottom">
|
||||
<button class="uk-button uk-button-primary"
|
||||
[disabled]="!privacyAccepted || destinationPath.length == 0 || status == 'succeeded'
|
||||
|| (requests > 0) || !validatePath() || !validateDestinationUrl() || (!this.downloadElements || this.downloadElements.length == 0)"
|
||||
[class.uk-disabled]="!privacyAccepted || destinationPath.length == 0 || status == 'succeeded'
|
||||
|| (requests > 0) || !validatePath() || !validateDestinationUrl() || (!this.downloadElements || this.downloadElements.length == 0)"
|
||||
(click)="transfer()">
|
||||
>> Transfer
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="status == 'loading'" class="uk-flex uk-flex-center uk-text-muted">
|
||||
<div>
|
||||
<span class="uk-icon uk-spinner">
|
||||
<svg width="60" height="60" viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg"
|
||||
data-svg="spinner"><circle
|
||||
fill="none" stroke="#000" cx="15" cy="15" r="14" style="stroke-width: 2px;"></circle></svg>
|
||||
</span>
|
||||
<!-- [class.uk-alert-success]="status && status.indexOf('error')==-1"-->
|
||||
<!-- [class.uk-alert-error]="status && status.indexOf('error')!=-1">-->
|
||||
<div id="transferAlert" *ngIf="message" class="uk-width-1-1 uk-alert uk-margin-medium-top" [ngClass]="'uk-alert-'+statusMessage" uk-alert>
|
||||
<div [innerHTML]="message"></div>
|
||||
<!-- <a *ngIf="status != 'succeeded' && status != 'active'" (click)="transfer()">-->
|
||||
<!-- Try again!-->
|
||||
<!-- </a>-->
|
||||
<div *ngIf="requests > 0" class="uk-flex uk-flex-center uk-text-muted">
|
||||
<div>
|
||||
<span class="uk-icon uk-spinner">
|
||||
<svg width="60" height="60" viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg"
|
||||
data-svg="spinner"><circle
|
||||
fill="none" stroke="#000" cx="15" cy="15" r="14" style="stroke-width: 2px;"></circle></svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="message" class="uk-width-1-1 uk-alert"
|
||||
[class.uk-alert-success]="status && status.indexOf('error')==-1"
|
||||
[class.uk-alert-error]="status && status.indexOf('error')!=-1"
|
||||
[innerHTML]="message">
|
||||
<a *ngIf="requests <= 0" class="uk-alert-close" uk-close></a>
|
||||
</div>
|
||||
<!-- <div *ngIf="jobId || status == 'canceled'">
|
||||
<button *ngIf=" status != 'canceled'" class="uk-button uk-button-default" (click)="getStatus()">Check status</button>
|
||||
<button *ngIf=" status != 'canceled'" class="uk-button uk-button-default" (click)="cancel()">Cancel</button>
|
||||
<div>{{statusMessage}}</div>
|
||||
</div>-->
|
||||
<div *ngIf=" status != 'canceled'" class="uk-width-1-1 uk-text-right "
|
||||
[class.uk-invisible]="!(status == 'success' || status.indexOf('error')!=-1)">
|
||||
<button class="uk-button uk-button-default uk-margin-top " (click)="close()">Close</button>
|
||||
</div>
|
||||
<!-- <div *ngIf=" status != 'canceled'" class="uk-width-1-1 uk-text-right "-->
|
||||
<!-- [class.uk-invisible]="!(status == 'succeeded' || status.indexOf('error')!=-1)">-->
|
||||
<!-- <button class="uk-button uk-button-default uk-margin-top " (click)="close()">Close</button>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<!-- TESTS -->
|
||||
<!-- <button (click)="hasBrowse()"
|
||||
|
@ -108,7 +155,7 @@
|
|||
</button>-->
|
||||
<!-- End of TESTS-->
|
||||
</div>
|
||||
</modal-alert>
|
||||
</fs-modal>
|
||||
|
||||
<!-- Browse Templates -->
|
||||
<ng-template #folderListTmpl let-folder="folder" let-folderPath="folderPath" >
|
||||
|
@ -116,7 +163,8 @@
|
|||
<ul *ngIf="folders[folderPath] && folders[folderPath].isOpen" class="uk-list uk-list-divider uk-margin-small-left folder " style="border-left: 1px solid #e3e3e3; padding-left: 5px;">
|
||||
<li *ngFor=" let file of files[folderPath]">
|
||||
<ng-container *ngIf="file.isFolder else itsFile">
|
||||
<ng-container *ngTemplateOutlet = "folderListTmpl; context:{ folder: file, folderPath:file.accessUrl.split(selectedDestination.url)[1]}"></ng-container>
|
||||
<ng-container *ngTemplateOutlet = "folderListTmpl; context:{ folder: file,
|
||||
folderPath:file.accessUrl.split(destinationUrl)[1]}"></ng-container>
|
||||
</ng-container>
|
||||
<ng-template #itsFile>
|
||||
<ng-container *ngTemplateOutlet="fileTmpl; context: { file: file}"></ng-container>
|
||||
|
@ -143,7 +191,7 @@
|
|||
title="Expand/ Collapse"></span>
|
||||
<span (click)="destinationPath = folderPath" title="Select folder">
|
||||
<span uk-icon="folder"></span> <span
|
||||
class="uk-width-expand uk-text-truncate uk-margin-small-left">{{folder.name?folder.name:folder.accessUrl.split(selectedDestination.url)[1]}}</span>
|
||||
class="uk-width-expand uk-text-truncate uk-margin-small-left">{{folder.name?folder.name:folder.accessUrl.split(destinationUrl)[1]}}</span>
|
||||
<!--<span class="uk-width-auto uk-text-truncate">{{(folder.createdAt?folder.createdAt:folder.modifiedAt) |date : 'medium'}}</span>-->
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import {Component, Input, ViewChild} from '@angular/core';
|
||||
import {Subscriber} from "rxjs";
|
||||
import {ChangeDetectorRef, Component, Input, ViewChild} from '@angular/core';
|
||||
import {interval, Subscriber, Subscription} from "rxjs";
|
||||
import {HttpClient, HttpHeaders} from "@angular/common/http";
|
||||
import {AbstractControl, ValidatorFn, Validators} from "@angular/forms";
|
||||
import {Location} from '@angular/common';
|
||||
import {COOKIE} from "../../login/utils/helper.class";
|
||||
import {Router} from "@angular/router";
|
||||
import {properties} from "../../../../environments/environment";
|
||||
import {delay, repeat} from "rxjs/operators";
|
||||
import {delay, repeat, startWith, switchMap} from "rxjs/operators";
|
||||
import {StringUtils} from "../string-utils.class";
|
||||
import {HelperFunctions} from "../HelperFunctions.class";
|
||||
import {FullScreenModalComponent} from "../modal/full-screen-modal/full-screen-modal.component";
|
||||
declare var UIkit;
|
||||
|
||||
@Component({
|
||||
|
@ -33,30 +36,58 @@ declare var UIkit;
|
|||
})
|
||||
export class EGIDataTransferComponent {
|
||||
subscriptions = [];
|
||||
statusSub: Subscription[] = [];
|
||||
accessToken = null;
|
||||
@Input() dois;
|
||||
loginURL = properties.environment == "development"? "http://rudie.di.uoa.gr:8580/openid_connect_login":"https://explore.eosc-portal.eu/egi-login-service/openid_connect_login"
|
||||
loginURL = properties.eoscDataTransferLoginUrl;
|
||||
sourceUrls = [];
|
||||
selectedSourceUrl = null;
|
||||
destinationUrl = "";
|
||||
destinationAuthUser = "";
|
||||
destinationAuthPass = "";
|
||||
destinationPath = "";
|
||||
destinationOptions = properties.eoscDataTransferDestinations;
|
||||
selectedDestination = null;
|
||||
destinationOptions = null;//properties.eoscDataTransferDestinations.map(dest => {return {"label": dest.destination, "value": dest}});
|
||||
selectedDestination:{ kind: string, destination: string, description: string, authType: 'token' | 'password' | 'keys',
|
||||
protocol: string, canBrowse: boolean, transferWith: string} = null;
|
||||
folders = {};
|
||||
files = {};
|
||||
requests: number = 0;
|
||||
downloadElements = null;
|
||||
@Input() isOpen = false;
|
||||
@Input() selectedDestinationId = "dcache";
|
||||
@ViewChild('egiTransferModal') egiTransferModal;
|
||||
// @Input() selectedDestinationId = "dcache";
|
||||
@ViewChild('egiTransferModal') egiTransferModal: FullScreenModalComponent;
|
||||
APIURL = properties.eoscDataTransferAPI;
|
||||
status: "loading" | "success" | "errorParser" | "errorUser" | "errorTransfer" | "init" | "canceled" = "init";
|
||||
// status: "loading" | "success" | "errorParser" | "errorUser" | "errorTransfer" | "init" | "canceled" = "init";
|
||||
status: "unused" | "staging" | "submitted" | "active" | "succeeded" | "partial" | "failed" | "canceled" | "errorParser" | "errorUser" | "init" = "init";
|
||||
|
||||
// unused("unused"),
|
||||
// active("active"),
|
||||
// succeeded("succeeded"),
|
||||
// failed("failed"),
|
||||
// canceled("canceled");
|
||||
|
||||
message;
|
||||
doiPrefix = properties.doiURL;
|
||||
validators = [Validators.required, this.pathValidator() /*StringUtils.urlValidator()*/];
|
||||
pathValidators = [Validators.required, this.pathValidator() /*StringUtils.urlValidator()*/];
|
||||
// URLValidators = [Validators.required, StringUtils.urlValidator()];
|
||||
URLValidators = [Validators.required];
|
||||
jobId = null;
|
||||
statusMessage = null;
|
||||
jobStatus;
|
||||
constructor(private http: HttpClient, private location: Location, private _router: Router) {
|
||||
|
||||
privacyAccepted: boolean = false;
|
||||
|
||||
public hostnameRegex = '[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|' +
|
||||
'[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|' +
|
||||
'[a-zA-Z0-9]+\.[^\s]{2,}|' +
|
||||
'[a-zA-Z0-9]+\.[^\s]{2,}'
|
||||
|
||||
constructor(private http: HttpClient, private location: Location, private _router: Router, private cdr: ChangeDetectorRef) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.URLValidators = [Validators.required, Validators.pattern(this.hostnameRegex)];
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
|
@ -71,25 +102,37 @@ export class EGIDataTransferComponent {
|
|||
subscription.unsubscribe();
|
||||
}
|
||||
});
|
||||
this.statusSub.forEach(sub => {
|
||||
if(sub instanceof Subscriber) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
open(){
|
||||
this.accessToken = COOKIE.getCookie("EGIAccessToken");
|
||||
if(this.accessToken) {
|
||||
if (this.selectedDestinationId) {
|
||||
for (let option of this.destinationOptions) {
|
||||
if (this.selectedDestinationId == option.value.id) {
|
||||
this.selectedDestination = option.value;
|
||||
}
|
||||
// if (this.selectedDestinationId) {
|
||||
// for (let option of this.destinationOptions) {
|
||||
// if (this.selectedDestinationId == option.value.destination) {
|
||||
// this.selectedDestination = option.value;
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// this.selectedDestination = this.destinationOptions[0].value;
|
||||
// }
|
||||
|
||||
let headers = new HttpHeaders({'Authorization': 'Bearer '+this.accessToken});
|
||||
this.subscriptions.push(this.http.get(this.APIURL + "/storage/types", {headers: headers}).subscribe(
|
||||
(res: Array<any>) => {
|
||||
this.destinationOptions = res.map(dest => {return {"label": dest.description, "value": dest}});
|
||||
this.selectedDestination = res[0];
|
||||
}
|
||||
} else {
|
||||
this.selectedDestination = this.destinationOptions[0].value;
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
for (let doi of this.dois) {
|
||||
if (doi.indexOf("zenodo.") != -1) {
|
||||
this.sourceUrls.push(this.doiPrefix + doi);
|
||||
}
|
||||
}
|
||||
try {
|
||||
this.sourceUrls.sort(function (a, b) {
|
||||
|
@ -103,13 +146,15 @@ export class EGIDataTransferComponent {
|
|||
this.parse();
|
||||
}
|
||||
this.isOpen = true;
|
||||
this.egiTransferModal.back = true;
|
||||
this.egiTransferModal.cancelButton = false;
|
||||
this.egiTransferModal.okButton = true;
|
||||
this.egiTransferModal.okButton = false;
|
||||
this.egiTransferModal.okButtonText = ">> Transfer";
|
||||
this.egiTransferModal.alertTitle = "EOSC data transfer service [demo]";
|
||||
this.egiTransferModal.stayOpen = true;
|
||||
this.egiTransferModal.title = "EOSC Data Transfer";
|
||||
this.init();
|
||||
this.egiTransferModal.open();
|
||||
if(typeof document !== 'undefined') {
|
||||
this.egiTransferModal.open();
|
||||
}
|
||||
}
|
||||
close(){
|
||||
if(this.isOpen) {
|
||||
|
@ -125,34 +170,52 @@ export class EGIDataTransferComponent {
|
|||
}
|
||||
init(){
|
||||
this.destinationPath = "";
|
||||
this.selectedDestination = this.destinationOptions[0].value;
|
||||
// this.selectedDestination = this.destinationOptions[0].value;
|
||||
this.selectedSourceUrl = this.sourceUrls[0];
|
||||
this.message = null;
|
||||
this.status = "init";
|
||||
this.jobId = null;
|
||||
this.statusMessage = null;
|
||||
// this.statusMessage = null;
|
||||
this.statusMessage = "primary";
|
||||
this.requests = 0;
|
||||
this.folders = {};
|
||||
this.files = {};
|
||||
this.statusSub.forEach(sub => {
|
||||
if(sub instanceof Subscriber) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
});
|
||||
}
|
||||
checkin(){
|
||||
window.location.href = this.loginURL+"?redirect="+ encodeURIComponent(window.location.href + (window.location.href.indexOf("&egiTransfer=t")!=-1?"":"&egiTransfer=t"));
|
||||
|
||||
}
|
||||
parse(){
|
||||
this.status = "loading";
|
||||
this.status = "active";
|
||||
this.message = null;
|
||||
this.downloadElements = [];
|
||||
this.subscriptions.push(this.http.get(this.APIURL + "/parser?doi=" + encodeURIComponent(this.selectedSourceUrl)).subscribe(
|
||||
let headers = new HttpHeaders({'Authorization': 'Bearer '+this.accessToken});
|
||||
this.subscriptions.push(this.http.get(this.APIURL + "/parser?doi=" + encodeURIComponent(this.selectedSourceUrl), {headers: headers}).subscribe(
|
||||
res => {
|
||||
// res['elements'].forEach(element => {
|
||||
// if(element.downloadUrl && element.name) {
|
||||
// this.downloadElements.push(element);
|
||||
// }
|
||||
// })
|
||||
this.downloadElements= res['elements']
|
||||
// console.log(this.downloadElements)
|
||||
this.status = "init";
|
||||
this.status = "init";
|
||||
}, error => {
|
||||
this.status = "errorParser";
|
||||
this.message = "Couldn't get download URLs from zenodo";
|
||||
UIkit.notification("Couldn't get download URLs from zenodo", {
|
||||
this.message = error.error && error.error.id && error.error.id == 'doiNotSupported'?'DOI not supported.':( error.error && error.error.description && error.error.description? (error.error.description+'.'):'Error parsing information.') ;
|
||||
this.statusMessage = "danger";
|
||||
this.cdr.detectChanges();
|
||||
HelperFunctions.scrollToId("transferAlert");
|
||||
/* UIkit.notification(this.message, {
|
||||
status: 'error',
|
||||
timeout: 3000,
|
||||
pos: 'bottom-right'
|
||||
});
|
||||
});*/
|
||||
|
||||
}
|
||||
));
|
||||
|
@ -161,9 +224,11 @@ export class EGIDataTransferComponent {
|
|||
|
||||
transfer() {
|
||||
// console.log(this.selectedDestination)
|
||||
this.status = "loading";
|
||||
this.status = "active";
|
||||
this.message = "";
|
||||
let headers = new HttpHeaders({'Authorization': 'Bearer '+this.accessToken});
|
||||
this.subscriptions.push(this.http.get(this.APIURL + "/user/info?dest="+this.selectedDestination.id, {headers: headers}).subscribe(
|
||||
|
||||
this.subscriptions.push(this.http.get(this.APIURL + "/user/info?dest="+this.selectedDestination.destination, {headers: headers}).subscribe(
|
||||
res => {
|
||||
// console.log(res)
|
||||
let body = {
|
||||
|
@ -177,10 +242,9 @@ export class EGIDataTransferComponent {
|
|||
|
||||
// console.log(this.selectedDestination)
|
||||
for (let element of this.downloadElements) {
|
||||
|
||||
let file = {
|
||||
"sources": [element['downloadUrl']],
|
||||
"destinations": [this.selectedDestination.url + this.destinationPath + element.name],
|
||||
"destinations": [(this.selectedDestination.protocol+"://") + this.destinationUrl + this.destinationPath + (this.destinationPath.endsWith("/") ? "" : "/") + ((element.path && element.path != "/") ? element.path : "") + element.name],
|
||||
|
||||
};
|
||||
//TODO priority? checksum? filesize?
|
||||
|
@ -189,20 +253,28 @@ export class EGIDataTransferComponent {
|
|||
}
|
||||
|
||||
let headers = new HttpHeaders({'Authorization': 'Bearer '+this.accessToken});
|
||||
if(this.selectedDestination.authType != "token" && this.destinationAuthPass.length > 0 && this.destinationAuthUser.length > 0){
|
||||
headers = new HttpHeaders({'Authorization': 'Bearer '+this.accessToken,
|
||||
'Authorization-Storage': btoa(this.destinationAuthUser + ':' + this.destinationAuthPass)});
|
||||
}
|
||||
this.subscriptions.push(this.http.post(this.APIURL + "/transfers" ,body, {headers: headers}).subscribe(
|
||||
res => {
|
||||
// console.log(res)
|
||||
UIkit.notification('Data transfer has began! ', {
|
||||
status: 'success',
|
||||
timeout: 6000,
|
||||
pos: 'bottom-right'
|
||||
});
|
||||
// UIkit.notification('Data transfer has began! ', {
|
||||
// status: 'success',
|
||||
// timeout: 6000,
|
||||
// pos: 'bottom-right'
|
||||
// });
|
||||
|
||||
this.jobId = res['jobId'];
|
||||
this.status = "success";
|
||||
this.egiTransferModal.okButton = false;
|
||||
this.getStatus();
|
||||
this.status = "active";
|
||||
this.statusMessage = "primary";
|
||||
|
||||
// this.egiTransferModal.okButton = false;
|
||||
this.message = `
|
||||
<!--div class="uk-text-large uk-margin-bottom">Data transfer has began!</div-->
|
||||
<div>Transfer of ` + this.downloadElements.length + ` files to <a href="`+ this.selectedDestination.webpage + `" target=_blank>`+this.selectedDestination.label+`</a> has began.`;
|
||||
<div>Transfer of ` + this.downloadElements.length + ` files to `+this.selectedDestination.description+` has began.`;
|
||||
/*this.message += ` <div class=" uk-overflow-auto uk-height-xsmall" >
|
||||
<ul>
|
||||
`;
|
||||
|
@ -219,27 +291,37 @@ export class EGIDataTransferComponent {
|
|||
this.message += `
|
||||
|
||||
</div>`
|
||||
// this.getStatus(true)
|
||||
|
||||
this.cdr.detectChanges();
|
||||
HelperFunctions.scrollToId("transferAlert");
|
||||
|
||||
// this.getStatus(true)
|
||||
|
||||
}, error => {
|
||||
this.status = "errorTransfer";
|
||||
this.message = "Couldn't transfer files";
|
||||
UIkit.notification("Couldn't transfer files", {
|
||||
status: 'error',
|
||||
timeout: 6000,
|
||||
pos: 'bottom-right'
|
||||
});
|
||||
this.status = "failed";
|
||||
this.message = "Files could not be transfered.";
|
||||
this.statusMessage = "danger";
|
||||
this.cdr.detectChanges();
|
||||
HelperFunctions.scrollToId("transferAlert");
|
||||
// UIkit.notification("Couldn't transfer files", {
|
||||
// status: 'error',
|
||||
// timeout: 6000,
|
||||
// pos: 'bottom-right'
|
||||
// });
|
||||
|
||||
}
|
||||
));
|
||||
}, error => {
|
||||
this.status = "errorUser";
|
||||
this.message = "User can't be authenticated!";
|
||||
UIkit.notification("User can't be authenticated!", {
|
||||
status: 'error',
|
||||
timeout: 6000,
|
||||
pos: 'bottom-right'
|
||||
});
|
||||
this.message = "User cannot be authenticated.";
|
||||
this.statusMessage = "danger";
|
||||
this.cdr.detectChanges();
|
||||
HelperFunctions.scrollToId("transferAlert");
|
||||
// UIkit.notification("User can't be authenticated!", {
|
||||
// status: 'error',
|
||||
// timeout: 6000,
|
||||
// pos: 'bottom-right'
|
||||
// });
|
||||
|
||||
}
|
||||
));
|
||||
|
@ -247,50 +329,131 @@ export class EGIDataTransferComponent {
|
|||
|
||||
getStatus(updateTransferMessage:boolean = false){
|
||||
if(this.jobId){
|
||||
|
||||
this.requests = 10;
|
||||
this.statusSub.forEach(sub => {
|
||||
if(sub instanceof Subscriber) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
let headers = new HttpHeaders({'Authorization': 'Bearer '+this.accessToken});
|
||||
let source = this.http.get(this.APIURL + "/transfer/" +this.jobId , {headers: headers}).pipe(delay(5000));
|
||||
this.subscriptions.push(source.pipe(repeat(3)).subscribe(
|
||||
res => {
|
||||
this.jobStatus = res;
|
||||
this.message = `
|
||||
<!--div class="uk-text-large uk-margin-bottom">Data transfer has began!</div-->
|
||||
<div>Transfer of ` + this.downloadElements.length + ` files to <a href="`+ this.selectedDestination.webpage + `" target=_blank>`+this.selectedDestination.label+`</a> has began.`;
|
||||
/*this.message += ` <div class=" uk-overflow-auto uk-height-xsmall" >
|
||||
<ul>
|
||||
`;
|
||||
// TODO LATER we can call status for each file and see if the transfer has been complete
|
||||
for(let element of this.downloadElements){
|
||||
// console.log(element)
|
||||
// this.message += ` <li> <a href="`+ this.selectedDestination.webpage + this.destinationPath + element.name + `" target="_blank">`+ element.name+ `</a></li> `;
|
||||
this.message += ` <li>`+ element.name+ `</li> `;
|
||||
}
|
||||
this.message += `
|
||||
</ul>
|
||||
</div>
|
||||
</div>`*/
|
||||
this.message += `
|
||||
|
||||
</div>`;
|
||||
console.log(res)
|
||||
this.statusMessage = res['jobState'] + (res['reason']?(" :" + res['reason']):"");
|
||||
UIkit.notification('got status! ', {
|
||||
status: 'success',
|
||||
timeout: 6000,
|
||||
pos: 'bottom-right'
|
||||
});
|
||||
|
||||
|
||||
let source2 = interval(5000) // request status every 5 secs
|
||||
.pipe(
|
||||
startWith(2000), // first call after 2 secs
|
||||
switchMap(() => this.http.get(this.APIURL + "/transfer/" +this.jobId , {headers: headers}))
|
||||
);
|
||||
|
||||
this.statusSub.push(source2.subscribe((res: any) => {
|
||||
this.requests--;
|
||||
if(this.status != res.jobState) {
|
||||
this.status = res.jobState;
|
||||
this.jobStatus = res;
|
||||
this.message = `
|
||||
<!--div class="uk-text-large uk-margin-bottom">Data transfer has began!</div-->
|
||||
<div>Transfer of ` + this.downloadElements.length + ` files to ` + this.selectedDestination.description + ` has began.`;
|
||||
/*this.message += ` <div class=" uk-overflow-auto uk-height-xsmall" >
|
||||
<ul>
|
||||
`;
|
||||
// TODO LATER we can call status for each file and see if the transfer has been complete
|
||||
for(let element of this.downloadElements){
|
||||
// console.log(element)
|
||||
// this.message += ` <li> <a href="`+ this.selectedDestination.webpage + this.destinationPath + element.name + `" target="_blank">`+ element.name+ `</a></li> `;
|
||||
this.message += ` <li>`+ element.name+ `</li> `;
|
||||
}
|
||||
this.message += `
|
||||
</ul>
|
||||
</div>
|
||||
</div>`*/
|
||||
this.message += `
|
||||
|
||||
</div>`;
|
||||
this.message += "<div>Transfer status: <b>"+res.jobState+"</b>.</div>";
|
||||
this.statusMessage = "primary";
|
||||
//this.statusMessage = res['jobState'] + (res['reason'] ? (" :" + res['reason']) : "");
|
||||
if(this.status == "partial") {
|
||||
this.message = "At least one of the selected files was successfully transfered.";
|
||||
this.statusMessage = "success";
|
||||
//this.statusSub.unsubscribe();
|
||||
this.statusSub.forEach(sub => {
|
||||
if(sub instanceof Subscriber) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
});
|
||||
this.requests = 0;
|
||||
} else if(this.status == "succeeded") {
|
||||
this.message = "Transfer successfully completed!";
|
||||
this.statusMessage = "success";
|
||||
//this.statusSub.unsubscribe();
|
||||
this.statusSub.forEach(sub => {
|
||||
if(sub instanceof Subscriber) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
});
|
||||
this.requests = 0;
|
||||
// UIkit.notification('Transfer successfully completed! ', {
|
||||
// status: 'success',
|
||||
// timeout: 6000,
|
||||
// pos: 'bottom-right'
|
||||
// });
|
||||
} else if(this.status == "failed") {
|
||||
this.message = "Transfer failed.";
|
||||
this.statusMessage = "danger";
|
||||
//this.statusSub.unsubscribe();
|
||||
this.statusSub.forEach(sub => {
|
||||
if(sub instanceof Subscriber) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
});
|
||||
this.requests = 0;
|
||||
// UIkit.notification('Transfer failed', {
|
||||
// status: 'danger',
|
||||
// timeout: 6000,
|
||||
// pos: 'bottom-right'
|
||||
// });
|
||||
} else if(this.requests <= 0 || (this.status != "active" && this.status != "submitted" && this.status != "staging")) {
|
||||
this.message = "Transfer status: <b>"+this.status+"</b>.";
|
||||
this.statusMessage = "warning";
|
||||
//this.statusSub.unsubscribe();
|
||||
this.statusSub.forEach(sub => {
|
||||
if(sub instanceof Subscriber) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
});
|
||||
this.requests = 0;
|
||||
// UIkit.notification('Transfer completed with status: '+this.status, {
|
||||
// status: 'warning',
|
||||
// timeout: 6000,
|
||||
// pos: 'bottom-right'
|
||||
// });
|
||||
}
|
||||
}
|
||||
if(this.requests <= 0) {
|
||||
this.message = "Transfer status: <b>"+this.status+"</b>.";
|
||||
this.statusMessage = "warning";
|
||||
//this.statusSub.unsubscribe();
|
||||
this.statusSub.forEach(sub => {
|
||||
if(sub instanceof Subscriber) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, error => {
|
||||
this.status = "errorTransfer";
|
||||
this.message = "Couldn't get status ";
|
||||
UIkit.notification("Couldn't get status", {
|
||||
status: 'error',
|
||||
timeout: 6000,
|
||||
pos: 'bottom-right'
|
||||
});
|
||||
this.status = "failed";
|
||||
this.message = "Status of the transfer could not be retrieved.";
|
||||
this.statusMessage = "danger";
|
||||
this.statusSub.forEach(sub => {
|
||||
if(sub instanceof Subscriber) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
});
|
||||
this.requests = 0;
|
||||
// UIkit.notification("Couldn't get status", {
|
||||
// status: 'error',
|
||||
// timeout: 6000,
|
||||
// pos: 'bottom-right'
|
||||
// });
|
||||
|
||||
}
|
||||
));
|
||||
|
@ -303,7 +466,6 @@ export class EGIDataTransferComponent {
|
|||
let headers = new HttpHeaders({'Authorization': 'Bearer '+this.accessToken});
|
||||
this.subscriptions.push(this.http.delete(this.APIURL + "/transfer/" +this.jobId , {headers: headers}).subscribe(
|
||||
res => {
|
||||
console.log(res);
|
||||
this.jobStatus = res;
|
||||
this.statusMessage = res['jobState'] + (res['reason']?(" :" + res['reason']):"");
|
||||
this.jobId = null;
|
||||
|
@ -314,7 +476,7 @@ export class EGIDataTransferComponent {
|
|||
}
|
||||
hasBrowse(){
|
||||
let headers = new HttpHeaders({'Authorization': 'Bearer '+this.accessToken});
|
||||
this.subscriptions.push(this.http.get(this.APIURL + "/storage/info?dest="+this.selectedDestination.id+"&seUrl="+encodeURIComponent(this.selectedDestination.url + this.destinationPath) , {headers: headers}).subscribe(
|
||||
this.subscriptions.push(this.http.get(this.APIURL + "/storage/info?dest="+this.selectedDestination.destination+"&seUrl="+encodeURIComponent((this.selectedDestination.protocol+'://') + this.destinationUrl + this.destinationPath) , {headers: headers}).subscribe(
|
||||
res => {
|
||||
console.log(res);
|
||||
}
|
||||
|
@ -324,7 +486,7 @@ export class EGIDataTransferComponent {
|
|||
getFolder(folderPath){
|
||||
//TODO is this necessary?
|
||||
let headers = new HttpHeaders({'Authorization': 'Bearer '+this.accessToken});
|
||||
this.subscriptions.push(this.http.get(this.APIURL + "/storage/folder?dest="+this.selectedDestination.id+"&seUrl="+encodeURIComponent(this.selectedDestination.url + folderPath) , {headers: headers}).subscribe(
|
||||
this.subscriptions.push(this.http.get(this.APIURL + "/storage/folder?dest="+this.selectedDestination.destination+"&seUrl="+encodeURIComponent((this.selectedDestination.protocol + '://') + this.destinationUrl + folderPath) , {headers: headers}).subscribe(
|
||||
res => {
|
||||
this.folders[folderPath]= res;
|
||||
this.folders[folderPath]['isOpen'] = true;
|
||||
|
@ -339,7 +501,7 @@ export class EGIDataTransferComponent {
|
|||
}
|
||||
this.getFolder(folderPath);
|
||||
let headers = new HttpHeaders({'Authorization': 'Bearer '+this.accessToken});
|
||||
this.subscriptions.push(this.http.get(this.APIURL + "/storage/folder/list?dest="+this.selectedDestination.id+"&folderUrl="+encodeURIComponent(this.selectedDestination.url + folderPath) , {headers: headers}).subscribe(
|
||||
this.subscriptions.push(this.http.get(this.APIURL + "/storage/folder/list?dest="+this.selectedDestination.destination+"&folderUrl="+encodeURIComponent((this.selectedDestination.protocol + '://') + this.destinationUrl + folderPath) , {headers: headers}).subscribe(
|
||||
res => {
|
||||
this.files[folderPath]= res['elements'];
|
||||
}
|
||||
|
@ -348,8 +510,8 @@ export class EGIDataTransferComponent {
|
|||
}
|
||||
createFolder(){
|
||||
let headers = new HttpHeaders({'Authorization': 'Bearer '+this.accessToken});
|
||||
this.subscriptions.push(this.http.post(this.APIURL + "/storage/folder?dest="+this.selectedDestination.id+"&seUrl="+
|
||||
encodeURIComponent(this.selectedDestination.url + this.destinationPath + "test1/") , {headers: headers}).subscribe(
|
||||
this.subscriptions.push(this.http.post(this.APIURL + "/storage/folder?dest="+this.selectedDestination.destination+"&seUrl="+
|
||||
encodeURIComponent(this.destinationUrl + this.destinationPath + "test1/") , {headers: headers}).subscribe(
|
||||
res => {
|
||||
console.log(res);
|
||||
}
|
||||
|
@ -359,7 +521,7 @@ export class EGIDataTransferComponent {
|
|||
deleteFolder(){
|
||||
|
||||
let headers = new HttpHeaders({'Authorization': 'Bearer '+this.accessToken});
|
||||
this.subscriptions.push(this.http.delete(this.APIURL + "/storage/folder?dest="+this.selectedDestination.id+"&seUrl="+encodeURIComponent(this.selectedDestination.url + this.destinationPath + "test1/") , {headers: headers}).subscribe(
|
||||
this.subscriptions.push(this.http.delete(this.APIURL + "/storage/folder?dest="+this.selectedDestination.destination+"&seUrl="+encodeURIComponent(this.destinationUrl + this.destinationPath + "test1/") , {headers: headers}).subscribe(
|
||||
res => {
|
||||
console.log(res);
|
||||
}
|
||||
|
@ -373,14 +535,23 @@ export class EGIDataTransferComponent {
|
|||
pathValidator(): ValidatorFn {
|
||||
return (control: AbstractControl): { [key: string]: string } | null => {
|
||||
if (!this.validatePath()) {
|
||||
return {'error': 'Path should start and end with "/" e.g /path/'};
|
||||
return {'error': 'Path should start with "/"'};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
validatePath():boolean {
|
||||
let exp1 = /^\/([A-z0-9-_+]+\/)*$/g;
|
||||
let exp1 = /^\/([A-z0-9-_+]+\/?)*$/g;
|
||||
return (this.destinationPath.length > 0 && this.destinationPath.match(exp1) != null)
|
||||
|
||||
}
|
||||
|
||||
validateDestinationUrl():boolean {
|
||||
return (this.destinationUrl.length > 0 && new RegExp(this.hostnameRegex).test(this.destinationUrl));
|
||||
}
|
||||
|
||||
// public sourceUrlValidators() {
|
||||
// this.URLValidators = [];
|
||||
// this.URLValidators = [Validators.required, Validators.pattern(this.hostnameRegex)];
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -4,11 +4,12 @@ import {FormsModule} from '@angular/forms';
|
|||
import {EGIDataTransferComponent} from "./transferData.component";
|
||||
import {InputModule} from "../../sharedComponents/input/input.module";
|
||||
import {AlertModalModule} from "../modal/alertModal.module";
|
||||
import {FullScreenModalModule} from "../modal/full-screen-modal/full-screen-modal.module";
|
||||
import {IconsModule} from '../icons/icons.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule, FormsModule, InputModule, AlertModalModule, IconsModule
|
||||
CommonModule, FormsModule, InputModule, AlertModalModule, FullScreenModalModule, IconsModule
|
||||
],
|
||||
declarations: [
|
||||
EGIDataTransferComponent
|
||||
|
|
|
@ -2,6 +2,9 @@ import {Email} from "./email";
|
|||
import {Body} from "./body";
|
||||
import {FormArray} from "@angular/forms";
|
||||
import {properties} from "../../../../environments/environment";
|
||||
import {Stakeholder} from "../../monitor/entities/stakeholder";
|
||||
import {Report} from "../../../../cache-indicators";
|
||||
import {error} from "protractor";
|
||||
|
||||
export class Composer {
|
||||
private static noteBodySize = "14px";
|
||||
|
@ -32,13 +35,14 @@ export class Composer {
|
|||
|
||||
email.subject = this.subjectPrefix + communityName + ": Welcome new manager";
|
||||
email.body = "<div style='font-size:" + this.noteBodySize + "'><p>Welcome to OpenAIRE Connect!</p>"
|
||||
+ "<p>You are receiving this e-mail as you were assigned as manager of the community <a href='https://beta."
|
||||
+ communityId + ".openaire.eu/'>" + communityName + "</a> dashboard. "
|
||||
+ "<p>You are receiving this e-mail as you were assigned as manager of the <a href='https://beta."
|
||||
+ communityId + ".openaire.eu/'>" + communityName + "</a> gateway. "
|
||||
+ "In order to access the administration section of your community you must first login using one of the available options. "
|
||||
+ "<br>The administrative rights are associated to the e-mail address, that was used to send you this message."
|
||||
+ "<br>The administrative rights are associated with the e-mail address that was used to send you this message."
|
||||
+ " If you login with an account associated to a different email, please <a href='mailto:helpdesk@openaire.eu'>contact us</a>"
|
||||
+ " or your colleagues, that already have access to the administration tool, to update your e-mail. <br>"
|
||||
+ "You can access the administration tool by clicking the \"Manage\" button that will be available in the top right corner of the dashboard."
|
||||
+ "If you do not know which is the email associated with your OpenAIRE account, you can follow the instructions of <a href='https://youtu.be/Z3ePc4ltJ4M?t=30'>this video (from 00:30 to 1:00).</a><br>"
|
||||
+ "Once logged in, you can access the administration tool by clicking the \"Manage\" button that will be available in the top right corner of the gateway."
|
||||
+ "</p>"
|
||||
+ this.signature
|
||||
+ "<p style='font-size:" + this.noteFontSize + "'>" + this.ifYouAreNotResponsible(properties.admins[0]) + "<p>"
|
||||
|
@ -89,7 +93,7 @@ export class Composer {
|
|||
+ "<span><b>Affiliation</b>: " + (contactForm.affiliation ? contactForm.affiliation : '-') + "</span><br>"
|
||||
+ "<p>" + contactForm.message + "</p>"
|
||||
+ "</div>";
|
||||
email.recipients = admins;
|
||||
email.recipients = admins;
|
||||
return email;
|
||||
}
|
||||
|
||||
|
@ -209,6 +213,19 @@ export class Composer {
|
|||
return email;
|
||||
}
|
||||
|
||||
public static composeEmailToReportCachingProcess(report: Report) {
|
||||
let email: Email = new Email();
|
||||
email.recipients = [report.creator];
|
||||
email.subject = 'OpenAIRE | Monitor Dashboard - ' + report.name + '\'s caching process has been finished';
|
||||
email.body = "<div style='font-size" + this.noteBodySize + "'>"
|
||||
+ "<p>"
|
||||
+ "<div>Success: " + report.success + "/" + report.total + "</div>"
|
||||
+ "<div>Errors: " + report.errors.map(error => error.url + ' - ' + error.status).join('<br>') + "</div>"
|
||||
+ "</p>"
|
||||
+ "</div>";
|
||||
return email;
|
||||
}
|
||||
|
||||
public static formatEmailBodyForInvitation(body: Body): string {
|
||||
let fromMessageAndName = "";
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ declare var ResizeObserver;
|
|||
<div class="uk-modal-dialog">
|
||||
<div #header class="uk-modal-header uk-flex uk-flex-middle" [ngClass]="classTitle">
|
||||
<div [class.uk-invisible]="!back" class="uk-width-medium@l uk-width-auto uk-flex uk-flex-center">
|
||||
<button class="uk-button uk-button-link" [disabled]="!back" (click)="backClicked()">
|
||||
<button class="uk-button uk-button-link" [class.uk-disabled]="!back" [disabled]="!back" (click)="backClicked()">
|
||||
<icon name="west" [flex]="true" [ratio]="isMobile?2:3"></icon>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -33,13 +33,14 @@ declare var ResizeObserver;
|
|||
<h4 *ngIf="isMobile" class="uk-margin-remove">{{title}}</h4>
|
||||
<h2 *ngIf="!isMobile" class="uk-margin-remove">{{title}}</h2>
|
||||
</div>
|
||||
<div class="uk-width-medium@l uk-width-auto uk-flex" [class.uk-flex-center]="okButton" [class.uk-flex-right]="!okButton">
|
||||
<div class="uk-width-medium@l uk-width-auto uk-flex"
|
||||
[class.uk-flex-center]="okButton" [class.uk-flex-right]="!okButton">
|
||||
<button *ngIf="okButton" class="uk-button uk-button-default" [disabled]="okButtonDisabled"
|
||||
[class.uk-disabled]="okButtonDisabled" (click)="ok()">
|
||||
{{okButtonText}}
|
||||
</button>
|
||||
<button *ngIf="!okButton" class="uk-close uk-icon" (click)="cancel()">
|
||||
<icon name="close" [ratio]="2"></icon>
|
||||
<button *ngIf="!okButton && cancelButton" class="uk-close uk-icon" (click)="cancel()">
|
||||
<icon name="close" ratio="2"></icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -64,6 +65,7 @@ export class FullScreenModalComponent implements AfterViewInit, OnDestroy {
|
|||
*/
|
||||
public stayOpenInBack: boolean = false;
|
||||
title: string;
|
||||
cancelButton: boolean = true;
|
||||
okButton: boolean = false;
|
||||
okButtonText = 'OK';
|
||||
isMobile: boolean = false;
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import {Pipe, PipeTransform} from "@angular/core";
|
||||
import {Level, NumberSize, NumberUtils} from "../number-utils.class";
|
||||
import {DecimalPipe} from "@angular/common";
|
||||
|
||||
@Pipe({name: 'numberPercentage'})
|
||||
export class NumberPercentagePipe implements PipeTransform {
|
||||
decimalPipe: DecimalPipe = new DecimalPipe("en");
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* */
|
||||
transform(value: number | string, ...args: any[]): any {
|
||||
let locale: string = 'en';
|
||||
if (args[0]) {
|
||||
locale = args[0];
|
||||
this.decimalPipe = new DecimalPipe(locale);
|
||||
} else {
|
||||
this.decimalPipe = new DecimalPipe('en');
|
||||
}
|
||||
value = Number.parseFloat(value.toString()) * 100;
|
||||
return this.decimalPipe.transform(value) + '<span class="number-size">%</span>';
|
||||
}
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
import {NgModule} from "@angular/core";
|
||||
import {NumberRoundPipe} from "./number-round.pipe";
|
||||
import {NumberPercentagePipe} from "./number-percentage.pipe";
|
||||
|
||||
@NgModule({
|
||||
declarations: [NumberRoundPipe],
|
||||
exports: [NumberRoundPipe]
|
||||
declarations: [NumberRoundPipe, NumberPercentagePipe],
|
||||
exports: [NumberRoundPipe, NumberPercentagePipe]
|
||||
|
||||
})
|
||||
export class NumberRoundModule {
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import {Pipe, PipeTransform} from "@angular/core";
|
||||
import {Level, NumberSize, NumberUtils} from "../number-utils.class";
|
||||
import {DecimalPipe} from "@angular/common";
|
||||
import {DecimalPipe, registerLocaleData} from "@angular/common";
|
||||
import l from '@angular/common/locales/eu';
|
||||
import e from '@angular/common/locales/extra/eu';
|
||||
|
||||
@Pipe({name: 'numberRound'})
|
||||
export class NumberRoundPipe implements PipeTransform {
|
||||
decimalPipe: DecimalPipe = new DecimalPipe("en");
|
||||
|
||||
constructor() {
|
||||
registerLocaleData(l, 'eu', e);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,12 +19,19 @@ export class NumberRoundPipe implements PipeTransform {
|
|||
transform(value: number | string, ...args: any[]): any {
|
||||
let level = Level.ALL;
|
||||
let decimal = 0;
|
||||
let locale: string = 'en';
|
||||
if (args[0]) {
|
||||
level = args[0];
|
||||
}
|
||||
if (args[1]) {
|
||||
decimal = args[1];
|
||||
}
|
||||
if (args[2]) {
|
||||
locale = args[2];
|
||||
this.decimalPipe = new DecimalPipe(locale);
|
||||
} else {
|
||||
this.decimalPipe = new DecimalPipe('en');
|
||||
}
|
||||
let size: NumberSize = NumberUtils.roundNumber(value, level, decimal);
|
||||
return this.decimalPipe.transform(size.number) + (size.size ? '<span class="number-size">' + size.size + '</span>' : '');
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export type Environment = "development" | "test" | "beta" | "production";
|
||||
export type Dashboard = "explore" | "connect" | "monitor" | "aggregator" | "eosc";
|
||||
export type Dashboard = "explore" | "connect" | "monitor" | "aggregator" | "eosc" | "client-management-portal";
|
||||
export type PortalType = "explore" | "connect" | "community" | "monitor" | "funder" | "ri" | "project" | "organization" | "aggregator" | "eosc";
|
||||
|
||||
export interface EnvProperties {
|
||||
|
@ -65,6 +65,7 @@ export interface EnvProperties {
|
|||
registryUrl?: string;
|
||||
logoutUrl?: string;
|
||||
userInfoUrl?: string;
|
||||
clientManagementUrl?: string,
|
||||
cookieDomain?: string;
|
||||
feedbackmail?: string;
|
||||
feedbackmailForMissingEntities?: string;
|
||||
|
@ -145,5 +146,7 @@ export interface EnvProperties {
|
|||
egiNotebookLink?: string;
|
||||
connectPortalUrl?;
|
||||
eoscDataTransferAPI?;
|
||||
eoscDataTransferLoginUrl?;
|
||||
eoscDataTransferDestinations?;
|
||||
hasMachineCache?: boolean;
|
||||
}
|
||||
|
|
|
@ -232,7 +232,11 @@ export class ResultPreviewComponent implements OnInit, OnChanges {
|
|||
|
||||
public addEoscPrevInParams(obj) {
|
||||
if(properties.adminToolsPortalType == "eosc" && this.prevPath) {
|
||||
return this.routerHelper.addQueryParam("pv", this.prevPath, obj);
|
||||
let splitted: string[] = this.prevPath.split("?");
|
||||
obj = this.routerHelper.addQueryParam("return_path", splitted[0], obj);
|
||||
if(splitted.length > 0) {
|
||||
obj = this.routerHelper.addQueryParam("search_params", splitted[1], obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
|
|
@ -122,12 +122,12 @@ export class ISVocabulariesService {
|
|||
|
||||
getFos(properties: EnvProperties): Observable<any> {
|
||||
let url = "/assets/common-assets/vocabulary/fos.json";
|
||||
return this.http.get(url);
|
||||
return this.http.get(url).pipe(map(res => (res && res['fos']) ? res : {fos: []}));
|
||||
}
|
||||
|
||||
getSDGs(properties: EnvProperties): Observable<any> {
|
||||
let url = "/assets/common-assets/vocabulary/sdg.json";
|
||||
return this.http.get(url);
|
||||
return this.http.get(url).pipe(map(res => (res && res['sdg'])?res:{sdg: []}));
|
||||
}
|
||||
|
||||
parseSDGs(data: any): AutoCompleteValue[] {
|
||||
|
|
|
@ -266,6 +266,8 @@ export class StringUtils {
|
|||
'[a-zA-Z0-9]+\\.[^\\s]{2,}';
|
||||
|
||||
public static routeRegex = '^[a-zA-Z0-9\/][a-zA-Z0-9\/-]*$';
|
||||
|
||||
public static jsonRegex = /^[\],:{}\s]*$/;
|
||||
|
||||
public static urlPrefix(url: string): string {
|
||||
if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("//")) {
|
||||
|
@ -325,6 +327,19 @@ export class StringUtils {
|
|||
public static urlValidator(): ValidatorFn {
|
||||
return Validators.pattern(StringUtils.urlRegex);
|
||||
}
|
||||
|
||||
public static jsonValidator(error: string = ''): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationErrors | null => {
|
||||
if(control.value) {
|
||||
let test = control.getRawValue().replace(/\\["\\\/bfnrtu]/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, '');
|
||||
console.log('test')
|
||||
if(!new RegExp(this.jsonRegex).test(test)) {
|
||||
return {error: 'Please provide a valid JSON.' + error}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
public static validatorType(options: string[]): ValidatorFn {
|
||||
return (control: AbstractControl): { [key: string]: boolean } | null => {
|
||||
|
|
|
@ -558,6 +558,9 @@
|
|||
<progress class="uk-progress" value="45" max="100">45%</progress>
|
||||
|
||||
</div>
|
||||
<div class="uk-width-expand@m">
|
||||
<div class="uk-progress-circle" percentage="45" style="--percentage: 45"></div>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<button type="button" class="uk-icon uk-close">
|
||||
|
|
Loading…
Reference in New Issue