ui resrtucturing

This commit is contained in:
Diamantis Tziotzios 2019-01-18 19:03:45 +02:00
parent 4845ba84a7
commit 41fb94eee9
863 changed files with 19949 additions and 17007 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><root><dmp><dmpName>DMP Demodfgdfg</dmpName><projectName>OpenAIREplus</projectName><organisations><organisation name="Nord University" reference="organizationrepo:cristin/204"/><organisation name="Hanne Moa" reference="personrepo:orcid-sandbox/0000-0003-2050-142X"/></organisations><researchers/><datasets/></dmp></root>

File diff suppressed because one or more lines are too long

View File

@ -37,5 +37,4 @@ public class WebMVCConfiguration extends WebMvcConfigurerAdapter {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor(this.apiContext.getHelpersService().getLoggerService()));
}
}

View File

@ -48,7 +48,7 @@ public class DynamicProjectConfigurationDevelImpl implements DynamicProjectConfi
JAXBContext jaxbContext = JAXBContext.newInstance(Configuration.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
is = new URL("file:///"+current + "/dmp-backend/web/src/main/resources/ProjectConfiguration.xml").openStream();
is = new URL("file:///"+current + "/web/src/main/resources/ProjectConfiguration.xml").openStream();
this.configuration = (Configuration) jaxbUnmarshaller.unmarshal(is);
} catch (Exception ex) {
ex.printStackTrace();

View File

@ -31,7 +31,7 @@ public class DevelConfigLoader implements ConfigLoader {
JAXBContext jaxbContext = JAXBContext.newInstance(ExternalUrls.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
is = new URL("file:///"+current+"/dmp-backend/web/src/main/resources/ExternalUrls.xml").openStream();
is = new URL("file:///"+current+"/web/src/main/resources/ExternalUrls.xml").openStream();
externalUrls = (ExternalUrls) jaxbUnmarshaller.unmarshal(is);
} catch (Exception ex) {

View File

@ -47,7 +47,7 @@ public class FacebookTokenValidator implements TokenValidator {
user.setEmail(profile.getEmail());
user.setId(profile.getId());
user.setIsVerified(profile.isVerified());
//user.setIsVerified(profile.isVerified());
user.setName(profile.getName());
user.setProvider(TokenValidatorFactoryImpl.LoginProvider.FACEBOOK);
String url = (String)((Map<String,Object> )((Map<String,Object> )profile.getExtraData().get("picture")).get("data")).get("url");

View File

@ -1,9 +1,9 @@
dmp.domain = http://localhost:4200
####################PERSISTENCE OVERRIDES CONFIGURATIONS##########
database.url=
database.username=
database.password=
database.url=jdbc:postgresql://dbserver02.local.cite.gr:5432/dmptool
database.username=dmtadm
database.password=t00L4DM@18!
spring.datasource.maxIdle: 2
spring.datasource.max-active: 4
spring.datasource.max-wait: 10000

View File

@ -1,9 +1,9 @@
dmp.domain = https://opendmp.eu
####################PERSISTENCE OVERRIDES CONFIGURATIONS##########
database.url=
database.username=
database.password=
production.database.url=jdbc:postgresql://dl009.madgik.di.uoa.gr:5432/dmptool
production.database.username=dmpdbad
production.database.password=kWd4DmPd8@08!2
####################ELASTIIC SEARCH TAGS OVERRIDES CONFIGURATIONS##########
elasticsearch.host = tags-elastic-search
@ -22,7 +22,7 @@ configuration.h2020template=/tmp/h2020.docx
####################SPRING MAIL CONFIGURATIONS#################
spring.mail.default-encoding=UTF-8
spring.mail.host=
spring.mail.host=dit-gate.di.uoa.gr
spring.mail.username=
spring.mail.password=
spring.mail.port=25
@ -32,26 +32,26 @@ spring.mail.properties.mail.smtp.auth=false
spring.mail.properties.mail.smtp.starttls.enable=true
#############FACEBOOK LOGIN CONFIGURATIONS#########
facebook.login.clientId=
facebook.login.clientSecret=
facebook.login.namespace=
facebook.login.clientId=613977555670785
facebook.login.clientSecret=b656eb30077454dea7b3cfd03b6e4310
facebook.login.namespace=opendmp
#############GOOGLE LOGIN CONFIGURATIONS#########
google.login.clientId=
google.login.clientId=596924546661-83nhl986pnrpug5h624i5kptuao03dcd.apps.googleusercontent.com
#############LINKEDIN LOGIN CONFIGURATIONS#########
linkedin.login.clientId=
linkedin.login.clientSecret=
linkedin.login.clientId=86w8xorrsdzjud
linkedin.login.clientSecret=I9t9cjUgDNehcIIi
linkedin.login.redirect_uri=https://opendmp.eu/login/linkedin
#############TWITTER LOGIN CONFIGURATIONS#########
twitter.login.clientId=
twitter.login.clientSecret=
twitter.login.clientId=FBU0tH4J1wgmtJOuBOg0lKkmi
twitter.login.clientSecret=vDUUabrT8NYgAMKdA0ixWH8QQfjTqclXUPMb15LujgqBgg0Xsf
twitter.login.redirect_uri=https://opendmp.eu/login/twitter
#############B2 ACCESS CONFIGURATIONS#########
b2access.externallogin.user_info_url=https://b2access-integration.fz-juelich.de:443/oauth2/userinfo
b2access.externallogin.access_token_url=https://b2access-integration.fz-juelich.de:443/oauth2/token
b2access.externallogin.redirect_uri=https://opendmp.eu/api/oauth/authorized/b2access
b2access.externallogin.clientid=
b2access.externallogin.clientSecret=
b2access.externallogin.clientid=eudatdmptool
b2access.externallogin.clientSecret=A3b*1*92

View File

@ -1,9 +1,9 @@
dmp.domain = https://devel.opendmp.eu
####################PERSISTENCE OVERRIDES CONFIGURATIONS##########
database.url=
database.username=
database.password=
database.url=jdbc:postgresql://develdb1.madgik.di.uoa.gr:5432/dmptool
database.username=dmptool
database.password=dmpt00lu$r
####################ELASTIIC SEARCH TAGS OVERRIDES CONFIGURATIONS##########
elasticsearch.host = tags-elastic-search
@ -22,33 +22,33 @@ configuration.h2020template=/tmp/h2020.docx
####################SPRING MAIL CONFIGURATIONS#################
spring.mail.default-encoding=UTF-8
spring.mail.host=
spring.mail.host=dit-gate.di.uoa.gr
spring.mail.port=25
spring.mail.protocol=smtp
spring.mail.test-connection=false
spring.mail.properties.mail.smtp.auth=false
#############FACEBOOK LOGIN CONFIGURATIONS#########
facebook.login.clientId=
facebook.login.clientSecret=
facebook.login.namespace=
facebook.login.clientId=613977555670785
facebook.login.clientSecret=b656eb30077454dea7b3cfd03b6e4310
facebook.login.namespace=opendmp
#############GOOGLE LOGIN CONFIGURATIONS#########
google.login.clientId=
google.login.clientId=596924546661-83nhl986pnrpug5h624i5kptuao03dcd.apps.googleusercontent.com
#############LINKEDIN LOGIN CONFIGURATIONS#########
linkedin.login.clientId=
linkedin.login.clientSecret=
linkedin.login.clientId=86w8xorrsdzjud
linkedin.login.clientSecret=I9t9cjUgDNehcIIi
linkedin.login.redirect_uri=https://devel.opendmp.eu/login/linkedin
#############TWITTER LOGIN CONFIGURATIONS#########
twitter.login.clientId=
twitter.login.clientSecret=
twitter.login.clientId=FBU0tH4J1wgmtJOuBOg0lKkmi
twitter.login.clientSecret=vDUUabrT8NYgAMKdA0ixWH8QQfjTqclXUPMb15LujgqBgg0Xsf
twitter.login.redirect_uri=https://devel.opendmp.eu/login/twitter
#############B2 ACCESS CONFIGURATIONS#########
b2access.externallogin.user_info_url=https://b2access-integration.fz-juelich.de:443/oauth2/userinfo
b2access.externallogin.access_token_url=https://b2access-integration.fz-juelich.de:443/oauth2/token
b2access.externallogin.redirect_uri=https://devel.opendmp.eu/api/oauth/authorized/b2access
b2access.externallogin.clientid=
b2access.externallogin.clientSecret=
b2access.externallogin.clientid=eudatdmptool
b2access.externallogin.clientSecret=A3b*1*92

View File

@ -8,15 +8,15 @@ eu.eudat.logic.proxy.allowed.host=https://eestore.paas2.uninett.no
####################INVITATION MAIL CONFIGURATIONS##############
####################GENERIC MAIL CONFIGURATIONS#################
mail.subject=Invitation to DMP Plan {dmpname}
mail.from=
mail.from=citesagrdev@gmail.com
####################SPRING MAIL CONFIGURATIONS#################
spring.mail.default-encoding=UTF-8
spring.mail.host=
spring.mail.username=
spring.mail.password=
spring.mail.port=
spring.mail.protocol=
spring.mail.host=smtp.gmail.com
spring.mail.username=citesagrdev@gmail.com
spring.mail.password=w3l0v3CITe
spring.mail.port=587
spring.mail.protocol=smtp
spring.mail.test-connection=false
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
@ -28,29 +28,29 @@ autouser.root.password=root
autouser.root.username=root
#############FACEBOOK LOGIN CONFIGURATIONS#########
facebook.login.clientId=
facebook.login.clientSecret=
facebook.login.namespace=
facebook.login.clientId=110586756143149
facebook.login.clientSecret=522a847f05c873d0222c85109e24f55a
facebook.login.namespace=eudat
#############GOOGLE LOGIN CONFIGURATIONS#########
google.login.clientId=
google.login.clientId=524432312250-sc9qsmtmbvlv05r44onl6l93ia3k9deo.apps.googleusercontent.com
#############LINKEDIN LOGIN CONFIGURATIONS#########
linkedin.login.clientId=
linkedin.login.clientSecret=
linkedin.login.clientId=86bl8vfk77clh9
linkedin.login.clientSecret=2OCO9e3wKylW05Tt
linkedin.login.redirect_uri=http://opendmp.eu/login/linkedin
#############TWITTER LOGIN CONFIGURATIONS#########
twitter.login.clientId=
twitter.login.clientSecret=
twitter.login.clientId=HiR4hQH9HNubKC5iKQy0l4mAZ
twitter.login.clientSecret=9KZHgkqUO2QFnELSL14jeUvfUacWX23rqD8OW8X0xoRDXOSfKH
twitter.login.redirect_uri=http://opendmp.eu/login/twitter
#############B2 ACCESS CONFIGURATIONS#########
b2access.externallogin.user_info_url=https://b2access-integration.fz-juelich.de:443/oauth2/userinfo
b2access.externallogin.access_token_url=https://b2access-integration.fz-juelich.de:443/oauth2/token
b2access.externallogin.redirect_uri=http://opendmp.eu/api/oauth/authorized/b2access
b2access.externallogin.clientid=
b2access.externallogin.clientSecret=
b2access.externallogin.clientid=eudatdmptool
b2access.externallogin.clientSecret=A3b*1*92
#############FILE STORAGE CONFIGURATIONS#########
files.storage.temp = temp

View File

@ -1,51 +0,0 @@
import { BaseHttpService } from '../utilities/cite-http-service-module/base-http.service';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { TranslateLoader } from '@ngx-translate/core';
import { TranslateModule } from '@ngx-translate/core';
import { SharedModule } from '../shared/shared.module';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { AboutRoutes } from './about.routes';
import { AboutComponent } from './components/about.component';
@NgModule({
imports: [
CommonModule,
FormsModule,
SharedModule,
HttpClientModule,
AboutRoutes,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
],
declarations: [
AboutComponent
],
exports: [
AboutComponent
],
providers: [
BaseHttpService
]
})
export class AboutModule {
constructor(private translate: TranslateService) {
translate.setDefaultLang('en');
translate.use('en');
}
}
export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslateHttpLoader(httpClient, 'assets/lang/', '.json');
}

View File

@ -1,8 +0,0 @@
import { AboutComponent } from './components/about.component';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [
{ path: '', component: AboutComponent },
];
export const AboutRoutes = RouterModule.forChild(routes);

View File

@ -1,32 +0,0 @@
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>{{ 'ABOUT.TITLE' | translate}}</h1>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h3>{{ 'ABOUT.MAIN-CONTENT'| translate}}</h3>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h1>{{ 'ABOUT.CONTRIBUTORS'| translate}}</h1>
</div>
</div>
<div class="row">
<div class="col-md-5">
</div>
<div class="col-md-1">
<img src="/assets/images/logo_cite.png">
</div>
<div class="col-md-1">
<img src="/assets/images/logo_cite.png">
</div>
<div class="col-md-5">
</div>
</div>
</div>

View File

@ -1,22 +0,0 @@
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-about',
templateUrl: './about.component.html',
styleUrls: ['./about.component.scss'],
providers: [],
encapsulation: ViewEncapsulation.None
})
export class AboutComponent implements OnInit {
constructor() {
}
ngOnInit() {
}
}

View File

@ -1,116 +1,103 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomepageComponent } from './homepage/homepage.component';
import { AuthGuard } from './shared/guards/auth.guard';
import { WelcomepageComponent } from './welcomepage/welcomepage.component';
import { B2AccessLoginComponent } from './user-management/login/b2access/b2access-login.component';
const appRoutes: Routes = [
{
path: 'datasets',
loadChildren: './datasets/dataset.module#DatasetModule',
data: {
breadcrumb: true
},
//canActivate: [AuthGuard]
},
{
path: 'about',
loadChildren: './about/about.module#AboutModule',
data: {
breadcrumb: true
},
canActivate: [AuthGuard]
},
{
path: 'projects',
loadChildren: './projects/projects.module#ProjectsModule',
data: {
breadcrumb: true
},
canActivate: [AuthGuard]
},
{
path: 'dmps',
loadChildren: './dmps/dmps.module#DataManagementPlanModule',
data: {
breadcrumb: true
},
canActivate: [AuthGuard]
},
{
path: 'dmp-profiles',
loadChildren: './dmp-profiles/dmp-profile.module#DataManagamentPlanProfileModule',
data: {
breadcrumb: true
},
canActivate: [AuthGuard]
},
{
path: 'dataset-profile',
loadChildren: './dataset-profile-form/dataset-profile.module#DatasetProfileModule',
data: {
breadcrumb: true
},
canActivate: [AuthGuard]
},
{
path: 'home',
component: HomepageComponent,
data: {
breadcrumb: false
},
canActivate: [AuthGuard]
},
{
path: '',
redirectTo: '/welcome',
redirectTo: '/home',
data: {
breadcrumbs: false
},
pathMatch: 'full'
},
{
path: 'explore',
loadChildren: './ui/explore-dataset/explore-dataset.module#ExploreDatasetModule',
data: {
breadcrumb: true
}
},
{
path: 'datasets',
loadChildren: './ui/dataset/dataset.module#DatasetModule',
data: {
breadcrumb: true
}
},
{
path: 'about',
loadChildren: './ui/about/about.module#AboutModule',
data: {
breadcrumb: true
}
},
{
path: 'projects',
loadChildren: './ui/project/project.module#ProjectModule',
data: {
breadcrumb: true
}
},
{
path: 'plans',
loadChildren: './ui/dmp/dmp.module#DmpModule',
data: {
breadcrumb: true
}
},
{
path: 'dmp-profiles',
loadChildren: './ui/admin/dmp-profile/dmp-profile.module#DmpProfileModule',
data: {
breadcrumb: true
}
},
{
path: 'dataset-profiles',
loadChildren: './ui/admin/dataset-profile/dataset-profile.module#DatasetProfileModule',
data: {
breadcrumb: true
}
},
{
path: 'home',
loadChildren: './ui/dashboard/dashboard.module#DashboardModule',
data: {
breadcrumb: true
}
},
{
path: 'unauthorized',
loadChildren: './unauthorized/unauthorized.module#UnauthorizedModule',
loadChildren: './ui/misc/unauthorized/unauthorized.module#UnauthorizedModule',
data: {
breadcrumb: true
},
},
{
path: 'users',
loadChildren: './users/users.module#UsersModule',
loadChildren: './ui/admin/user/user.module#UserModule',
data: {
breadcrumb: true
},
},
{
path: 'welcome',
component: WelcomepageComponent,
path: 'login',
loadChildren: './ui/auth/login/login.module#LoginModule',
data: {
breadcrumb: false
breadcrumb: true
},
},
{
path: 'api/oauth/authorized/b2access',
component: B2AccessLoginComponent,
data: {
},
}
// {
// path: 'api/oauth/authorized/b2access',
// component: B2AccessLoginComponent,
// data: {
// },
// }
];
@NgModule({
imports: [
RouterModule.forRoot(
appRoutes
, {
useHash: false
}
)
],
exports: [
RouterModule
]
imports: [RouterModule.forRoot(appRoutes)],
exports: [RouterModule],
})
export class AppRoutingModule { }

View File

@ -1,15 +1,12 @@
.example-container {
background: rgb(250, 248, 248);
}
.fixed {
position: fixed;
top: 0; /* Sets the sticky toolbar to be on top */
position: fixed;
top: 0;
/* Sets the sticky toolbar to be on top */
z-index: 1000;
width: 100%;
}
.main-container{
.main-container {
margin-top: 64px;
padding-top: 10px;
}

View File

@ -1,23 +1,20 @@
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, ActivatedRoute, NavigationExtras, NavigationEnd } from '@angular/router';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { LanguageResolverService } from './services/language-resolver/language-resolver.service';
import { BreadCrumbResolverService } from './services/breadcrumb/breadcrumb-resolver.service';
import { Observable } from 'rxjs';
import { AuthService } from './services/auth/auth.service';
import { CultureService } from './utilities/culture/culture-service';
import { environment } from '../environments/environment';
import { AuthService } from './core/services/auth/auth.service';
import { CultureService } from './core/services/culture/culture-service';
import { BreadCrumbResolverService } from './ui/misc/breadcrumb/service/breadcrumb.service';
declare const gapi: any;
declare var $: any;
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
providers: [],
encapsulation: ViewEncapsulation.None
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
@ -30,7 +27,6 @@ export class AppComponent implements OnInit {
private route: ActivatedRoute,
private authentication: AuthService,
private translate: TranslateService,
private languageService: LanguageResolverService,
private breadCrumbResolverService: BreadCrumbResolverService,
private cultureService: CultureService
) {
@ -68,7 +64,7 @@ export class AppComponent implements OnInit {
}
goToDMPs() {
this.router.navigate(['/dmps'], { queryParams: { /*refresh : Math.random() ,returnUrl: this.state.url*/ } });
this.router.navigate(['/plans'], { queryParams: { /*refresh : Math.random() ,returnUrl: this.state.url*/ } });
}
goToProjects() {

View File

@ -1,63 +1,36 @@
import { CommonModule } from '@angular/common';
import { OverlayModule } from '@angular/cdk/overlay';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { LOCALE_ID, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { MAT_DATE_LOCALE } from '@angular/material';
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material';
import { MatMomentDateModule, MAT_MOMENT_DATE_FORMATS } from '@angular/material-moment-adapter';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HomepageComponent } from './homepage/homepage.component';
import { PageNotFoundComponent } from './not-found.component';
import { AuthService } from './services/auth/auth.service';
import { BreadCrumbResolverService } from './services/breadcrumb/breadcrumb-resolver.service';
import { DashboardService } from './services/dashboard/dashboard.service';
import { HelpContentService } from './services/help-content/help-content.service';
import { LanguageResolverService } from './services/language-resolver/language-resolver.service';
import { LanguageService } from './services/language/language.service';
import { AuthGuard } from './shared/guards/auth.guard';
import { AsideHelpContentComponent, HelpContentComponent } from './shared/help-content/help-content.component';
import { MaterialModule } from './shared/material/material.module';
import { SharedModule } from './shared/shared.module';
import { LoginModule } from './user-management/login.module';
import { B2AccessLoginComponent } from './user-management/login/b2access/b2access-login.component';
import { LoginOptions } from './user-management/utilties/LoginOptions';
import { RecentActivityComponent } from './users/activity/recent-activity.component';
import { BaseHttpService } from './utilities/cite-http-service-module/base-http.service';
import { BaseHttpModule } from './utilities/cite-http-service-module/cite-http.module';
import { CultureService } from './utilities/culture/culture-service';
import { UrlUtilities } from './utilities/UrlUtilities';
import { WelcomepageComponent } from './welcomepage/welcomepage.component';
import { MomentUtcDateAdapter } from './common/date/moment-utc-date-adapter';
import { CommonHttpModule } from './common/http/common-http.module';
import { CommonUiModule } from './common/ui/common-ui.module';
import { CoreServiceModule } from './core/core-service.module';
import { CultureService } from './core/services/culture/culture-service';
import { NotificationModule } from './library/notification/notification.module';
import { BreadcrumbModule } from './ui/misc/breadcrumb/breadcrumb.module';
import { HelpContentModule } from './ui/misc/help-content/help-content.module';
import { NavigationModule } from './ui/misc/navigation/navigation.module';
// AoT requires an exported function for factories
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
}
@NgModule({
declarations: [
AppComponent,
PageNotFoundComponent,
HomepageComponent,
RecentActivityComponent,
WelcomepageComponent,
HelpContentComponent,
AsideHelpContentComponent,
B2AccessLoginComponent,
],
imports: [
BrowserModule,
ReactiveFormsModule,
FormsModule,
HttpModule,
HttpClientModule,
BaseHttpModule.forRoot(),
CommonModule,
AppRoutingModule,
BrowserAnimationsModule,
CoreServiceModule.forRoot(),
AppRoutingModule,
CommonUiModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
@ -65,66 +38,33 @@ import { WelcomepageComponent } from './welcomepage/welcomepage.component';
deps: [HttpClient]
}
}),
BrowserAnimationsModule,
MaterialModule,
SharedModule,
LoginModule.forRoot({
loginProviders: [
LoginOptions.facebookOauth,
LoginOptions.googleOauth,
LoginOptions.linkedInOauth,
LoginOptions.twitterOauth,
LoginOptions.b2Access
],
facebookConfiguration: { clientId: environment.loginProviders.facebookConfiguration.clientId },
googleConfiguration: { clientId: environment.loginProviders.googleConfiguration.clientId },
linkedInConfiguration: {
clientId: environment.loginProviders.linkedInConfiguration.clientId,
oauthUrl: environment.loginProviders.linkedInConfiguration.oauthUrl,
redirectUri: environment.loginProviders.linkedInConfiguration.redirectUri,
},
twitterConfiguration: {
clientId: environment.loginProviders.twitterConfiguration.clientId,
oauthUrl: environment.loginProviders.twitterConfiguration.oauthUrl
},
b2accessConfiguration: {
clientId: environment.loginProviders.b2accessConfiguration.clientId,
oauthUrl: environment.loginProviders.b2accessConfiguration.oauthUrl,
redirectUri: environment.loginProviders.b2accessConfiguration.redirectUri
}
}),
HttpClientModule,
OverlayModule,
CommonHttpModule,
MatMomentDateModule,
//Ui
NotificationModule,
NavigationModule,
BreadcrumbModule,
HelpContentModule
],
declarations: [
AppComponent
],
providers: [
AuthGuard,
AuthService,
BaseHttpService,
UrlUtilities,
DashboardService,
HelpContentService,
LanguageService,
LanguageResolverService,
BreadCrumbResolverService,
CultureService,
{
provide: MAT_DATE_LOCALE,
deps: [CultureService],
useFactory: (cultureService) => cultureService.getCurrentCulture()
useFactory: (cultureService) => cultureService.getCurrentCulture().name
},
{ provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS },
{ provide: DateAdapter, useClass: MomentUtcDateAdapter },
{
provide: LOCALE_ID,
deps: [CultureService],
useFactory: (cultureService) => cultureService.getCurrentCulture()
useFactory: (cultureService) => cultureService.getCurrentCulture().name
},
],
bootstrap: [AppComponent]
})
export class AppModule {
constructor() {
}
}
export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslateHttpLoader(httpClient, 'assets/lang/', '.json');
}
export class AppModule { }

View File

@ -0,0 +1,44 @@
import { Inject, Injectable, Optional } from '@angular/core';
import { MAT_DATE_LOCALE } from '@angular/material';
import { MomentDateAdapter } from '@angular/material-moment-adapter';
import * as moment from 'moment';
import { Moment } from 'moment';
@Injectable()
export class MomentUtcDateAdapter extends MomentDateAdapter {
constructor(@Optional() @Inject(MAT_DATE_LOCALE) dateLocale: string) {
super(dateLocale);
}
// selected from datepicker
createDate(year: number, month: number, date: number): Moment {
// Moment.js will create an invalid date if any of the components are out of bounds, but we
// explicitly check each case so we can throw more descriptive errors.
if (month < 0 || month > 11) {
throw Error(`Invalid month index "${month}". Month index has to be between 0 and 11.`);
}
if (date < 1) {
throw Error(`Invalid date "${date}". Date has to be greater than 0.`);
}
const result = moment.utc({ year, month, date }).locale(this.locale);
// If the result isn't valid, the date must have been out of bounds for this month.
if (!result.isValid()) {
throw Error(`Invalid date "${date}" for month with index "${month}".`);
}
return result;
}
// manually writing on the textbox
parse(value: any, parseFormat: string | string[]): Moment | null {
const initialParse = super.parse(value, parseFormat);
if (!initialParse.isValid()) { return initialParse; }
const result = moment.utc({ year: initialParse.year(), month: initialParse.month(), date: initialParse.date() }).locale(this.locale);
return result;
}
}

View File

@ -0,0 +1,14 @@
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
FormsModule,
ReactiveFormsModule,
],
exports: [
FormsModule,
ReactiveFormsModule,
]
})
export class CommonFormsModule { }

View File

@ -0,0 +1,32 @@
import { AbstractControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
import { ValidationErrorModel } from './error-model/validation-error-model';
export function BackendErrorValidator(errorModel: ValidationErrorModel, propertyName: string): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } => {
const error: String = errorModel.getError(propertyName);
return error ? { 'backendError': { message: error } } : null;
};
}
export function E164PhoneValidator(): ValidatorFn {
return Validators.pattern('^\\+?[1-9]\\d{1,14}$');
}
// Getter is required because the index of each element is not fixed (array does not always follow LIFO)
export function BackendArrayErrorValidator(errorModel: ValidationErrorModel, propertyNameGetter: () => string): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } => {
const error: String = errorModel.getError(propertyNameGetter());
return error ? { 'backendError': { message: error } } : null;
};
}
export function CustomErrorValidator(errorModel: ValidationErrorModel, propertyNames: string[]): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } => {
const error: String = errorModel.getErrors(propertyNames);
return error ? { 'customError': { message: error } } : null;
};
}
export function PasswordMatchValidator(formGroup: FormGroup) {
return formGroup.get('password').value === formGroup.get('passwordConfirm').value ? null : { 'passwordMismatch': true };
}

View File

@ -0,0 +1,90 @@
import { Serializable } from '../../../types/json/serializable';
export class ValidationErrorModel implements Serializable<ValidationErrorModel> {
public error: string;
private message: Array<ErrorMessageItem>;
public fromJSONObject(item: any): ValidationErrorModel {
this.error = item.error;
this.message = item.message;
return this;
}
public getErrors(propertyNames: string[]): string {
const errors: string[] = [];
propertyNames.forEach(propertyName => {
const error = this.getError(propertyName);
if (error) { errors.push(error); }
});
return errors.join(', ');
}
public getError(propertyName: string): string {
let error: string;
if (this.message) {
for (const element of this.message) {
if (element.Key === propertyName) {
error = element.Value.join(', ');
break;
}
}
}
return error;
}
// errors by array index
public getErrorForArray(arrayProperty: string, fieldProperty: string): Map<number, string> {
const regExp = new RegExp(`^${arrayProperty}\\[([0-9]+)\\]\\.${fieldProperty}$`); // 1st group is index
const errors = new Map<number, string>();
if (this.message) {
this.message.forEach(element => {
const match = element.Key.match(regExp);
if (match && match.length >= 2) {
const index = Number.parseInt(match[1]);
errors.set(index, element.Value.join(', '));
}
});
}
return errors;
}
public setError(propertyName: string, error: string) {
if (this.message) {
let exists = false;
for (const element of this.message) {
if (element.Key === propertyName) {
if (!element.Value.includes(error)) { element.Value.push(error); }
exists = true;
break;
}
}
if (!exists) { this.message.push({ Key: propertyName, Value: [error] }); }
} else {
this.message = [{ Key: propertyName, Value: [error] }];
}
}
public clear() {
this.error = undefined;
if (this.message) {
this.message.forEach(element => {
element.Value.splice(0);
});
}
}
public clearPart(prefix: string) {
if (this.message) {
this.message.forEach(element => {
if (element && element.Key && element.Key.startsWith(prefix)) {
element.Value.splice(0);
}
});
}
}
}
class ErrorMessageItem {
Key: string;
Value: Array<string> = [];
}

View File

@ -0,0 +1,20 @@
import { ValidatorFn } from '@angular/forms';
export class ValidationContext {
validation: Validation[] = [];
getValidation(key: string): Validation {
for (let i = 0; i < this.validation.length; i++) {
if (this.validation[i].key === key) {
return this.validation[i];
}
}
throw new Error('Key ' + key + ' Was Not Found In The Validation Context');
}
}
export class Validation {
key: string;
validators?: ValidatorFn[] = new Array<ValidatorFn>();
descendantValidations?: ValidationContext;
}

View File

@ -0,0 +1,6 @@
import { HttpParams } from '@angular/common/http';
import { InterceptorContext } from './interceptor-context';
export class BaseHttpParams extends HttpParams {
interceptorContext?: InterceptorContext;
}

View File

@ -0,0 +1,54 @@
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { AuthTokenInterceptor } from './interceptors/auth-token.interceptor';
import { JsonInterceptor } from './interceptors/json.interceptor';
import { LocaleInterceptor } from './interceptors/locale.interceptor';
import { ProgressIndicationInterceptor } from './interceptors/progress-indication.interceptor';
import { RequestTimingInterceptor } from './interceptors/request-timing.interceptor';
import { ResponsePayloadInterceptor } from './interceptors/response-payload.interceptor';
import { UnauthorizedResponseInterceptor } from './interceptors/unauthorized-response.interceptor';
@NgModule({
imports: [
],
declarations: [
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthTokenInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: JsonInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: LocaleInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: UnauthorizedResponseInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: RequestTimingInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: ProgressIndicationInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: ResponsePayloadInterceptor,
multi: true,
}
]
})
export class CommonHttpModule { }

View File

@ -0,0 +1,10 @@
import { InterceptorType } from './interceptors/interceptor-type';
export class InterceptorContext {
// If an Interceptor is added here, it wont be used in this request context.
excludedInterceptors?: InterceptorType[] = [];
// If an Interceptor is added here, all requests including requests to external systems will be intercepted.
// By default only requests to the web application's services will be intercepted.
interceptAllRequests?: InterceptorType[] = [];
}

View File

@ -0,0 +1,26 @@
import { HttpEvent, HttpHandler, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { BaseInterceptor } from './base.interceptor';
import { InterceptorType } from './interceptor-type';
import { AuthService } from '../../../core/services/auth/auth.service';
@Injectable()
export class AuthTokenInterceptor extends BaseInterceptor {
constructor(
private authService: AuthService) { super(); }
get type(): InterceptorType { return InterceptorType.AuthToken; }
interceptRequest(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const authToken: string = this.authService.current() ? this.authService.current().token : null;
if (!authToken) { return next.handle(req); }
req = req.clone({
setHeaders: {
AuthToken: authToken
}
});
return next.handle(req);
}
}

View File

@ -0,0 +1,30 @@
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
import { BaseHttpParams } from '../base-http-params';
import { InterceptorType } from './interceptor-type';
import { environment } from '../../../../environments/environment';
export abstract class BaseInterceptor implements HttpInterceptor {
constructor() { }
abstract type: InterceptorType;
abstract interceptRequest(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>;
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.isApplied(req)) {
return this.interceptRequest(req, next);
}
return next.handle(req);
}
isApplied(req: HttpRequest<any>): boolean {
if (req.params instanceof BaseHttpParams && req.params.interceptorContext && Array.isArray(req.params.interceptorContext.excludedInterceptors) && req.params.interceptorContext.excludedInterceptors.includes(this.type)) {
return false;
}
return (req.params instanceof BaseHttpParams && req.params.interceptorContext && Array.isArray(req.params.interceptorContext.interceptAllRequests) && req.params.interceptorContext.interceptAllRequests.includes(this.type))
|| req.url.startsWith(environment.Server);
}
}

View File

@ -0,0 +1,9 @@
export enum InterceptorType {
AuthToken = 0,
JSONContentType = 1,
Locale = 2,
ProgressIndication = 3,
RequestTiming = 4,
UnauthorizedResponse = 5,
ResponsePayload = 5,
}

View File

@ -0,0 +1,22 @@
import { HttpEvent, HttpHandler, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { BaseInterceptor } from './base.interceptor';
import { InterceptorType } from './interceptor-type';
@Injectable()
export class JsonInterceptor extends BaseInterceptor {
constructor(
) { super(); }
get type(): InterceptorType { return InterceptorType.JSONContentType; }
interceptRequest(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (!req.headers.has('Content-Type')) { req = req.clone({ headers: req.headers.set('Content-Type', 'application/json') }); }
if (!req.headers.has('Accept')) { req = req.clone({ headers: req.headers.set('Accept', 'application/json') }); }
return next.handle(req);
}
}

View File

@ -0,0 +1,20 @@
import { HttpEvent, HttpHandler, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { BaseInterceptor } from './base.interceptor';
import { InterceptorType } from './interceptor-type';
@Injectable()
export class LocaleInterceptor extends BaseInterceptor {
constructor(
private language: TranslateService) { super(); }
get type(): InterceptorType { return InterceptorType.Locale; }
interceptRequest(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.language.currentLang) { req = req.clone({ headers: req.headers.set('Accept-Language', this.language.currentLang) }); }
return next.handle(req);
}
}

View File

@ -0,0 +1,25 @@
import { HttpEvent, HttpHandler, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { ProgressIndicationService } from '../../../core/services/progress-indication/progress-indication-service';
import { BaseInterceptor } from './base.interceptor';
import { InterceptorType } from './interceptor-type';
@Injectable()
export class ProgressIndicationInterceptor extends BaseInterceptor {
constructor(
private progressIndicationService: ProgressIndicationService) { super(); }
get type(): InterceptorType { return InterceptorType.ProgressIndication; }
interceptRequest(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.progressIndicationService.show();
return next
.handle(req).pipe(
finalize(() => {
this.progressIndicationService.dismiss();
}));
}
}

View File

@ -0,0 +1,32 @@
import { HttpEvent, HttpHandler, HttpRequest, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { LoggingService } from '../../../core/services/logging/logging-service';
import { BaseInterceptor } from './base.interceptor';
import { InterceptorType } from './interceptor-type';
@Injectable()
export class RequestTimingInterceptor extends BaseInterceptor {
constructor(
private logger: LoggingService) { super(); }
get type(): InterceptorType { return InterceptorType.RequestTiming; }
interceptRequest(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const started = Date.now();
return next
.handle(req).pipe(
tap(event => {
if (event instanceof HttpResponse) {
const elapsed = Date.now() - started;
if (req.method === 'POST') {
this.logger.info(`POST Request at ${req.url} with params: ${req.serializeBody()} took ${elapsed} ms.`);
} else {
this.logger.info(`${req.method} Request at ${req.urlWithParams} took ${elapsed} ms.`);
}
}
}));
}
}

View File

@ -0,0 +1,41 @@
import { HttpHandler, HttpHeaderResponse, HttpProgressEvent, HttpRequest, HttpResponse, HttpSentEvent, HttpUserEvent } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { BaseInterceptor } from './base.interceptor';
import { InterceptorType } from './interceptor-type';
@Injectable()
export class ResponsePayloadInterceptor extends BaseInterceptor {
constructor(
private snackBar: MatSnackBar
) { super(); }
get type(): InterceptorType { return InterceptorType.ResponsePayload; }
interceptRequest(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
return next.handle(req).pipe(
map(response => {
// if (!(response instanceof HttpResponse) || (response instanceof Blob)) { return response; }
// if (response.status == 200) {
// if (response.body.statusCode === ApiMessageCode.SUCCESS_MESSAGE) {
// //throw new Error('Request failed');
// // this.snackBar.openFromComponent(SnackBarNotificationComponent, {
// // data: { message: response['message'], language: null },
// // duration: 3000,
// // });
// return response.body.payload;
// } else if (response.body.statusCode === ApiMessageCode.NO_MESSAGE) {
// return response.body.payload;
// } else {
// return response.body.payload;
// }
// }
return response;
}));
}
}

View File

@ -0,0 +1,75 @@
import { HttpErrorResponse, HttpHandler, HttpHeaderResponse, HttpProgressEvent, HttpRequest, HttpResponse, HttpSentEvent, HttpUserEvent } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable, throwError } from 'rxjs';
import { catchError, mergeMap, tap } from 'rxjs/operators';
import { AuthService } from '../../../core/services/auth/auth.service';
import { BaseInterceptor } from './base.interceptor';
import { InterceptorType } from './interceptor-type';
@Injectable()
export class UnauthorizedResponseInterceptor extends BaseInterceptor {
constructor(
public router: Router,
private authService: AuthService,
) { super(); }
get type(): InterceptorType { return InterceptorType.UnauthorizedResponse; }
private accountRefresh$: Observable<Account> = null;
interceptRequest(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
return next.handle(req).pipe(
catchError(error => {
if (error instanceof HttpErrorResponse) {
switch ((<HttpErrorResponse>error).status) {
case 401:
return this.handle401Error(req, next);
default:
return throwError(error);
}
} else {
return throwError(error);
}
}));
}
private handle401Error(req: HttpRequest<any>, next: HttpHandler) {
if (!this.accountRefresh$) {
// this.accountRefresh$ = this.idpService.refreshToken().pipe(
// tap(account => {
// this.accountRefresh$ = null;
// if (!account) { throw throwError('missing_authentication_token'); }
// }),
// catchError(error => {
// this.logoutUser();
// return throwError(error);
// }));
}
return this.accountRefresh$.pipe(mergeMap(account => this.repeatRequest(account, req, next)));
}
private repeatRequest(account: Account, originalRequest: HttpRequest<any>, next: HttpHandler) {
const newAuthenticationToken: String = this.authService.current().token;
const newRequest = originalRequest.clone({
setHeaders: {
Authorization: `Bearer ${newAuthenticationToken}`
}
});
return next.handle(newRequest);
}
private logoutUser() {
//this.authService.clear();
if (!this.isLoginRoute() && !this.isSignupRoute()) { this.router.navigate(['/unauthorized']); }
}
private isLoginRoute(): boolean {
return this.router.isActive('login', false);
}
private isSignupRoute(): boolean {
return this.router.isActive('signup-register', false) || this.router.isActive('signup-invitation', false);
}
}

View File

@ -0,0 +1,107 @@
import { NgModule } from '@angular/core';
import {
MatButtonModule,
MatToolbarModule,
MatIconModule,
MatCardModule,
MatGridListModule,
MatSnackBarModule,
MatSidenavModule,
MatListModule,
MatChipsModule,
MatFormFieldModule,
MatSelectModule,
MatOptionModule,
MatInputModule,
MatExpansionModule,
MatAutocompleteModule,
MatProgressSpinnerModule,
MatTabsModule,
MatDialogModule,
MatMenuModule,
MatRadioModule,
MatStepperModule,
MatTooltipModule,
MatProgressBarModule,
MatCheckboxModule,
MatDatepickerModule,
MatButtonToggleModule,
MatSliderModule,
MatSlideToggleModule,
MatTableModule,
MatPaginatorModule,
MatSortModule,
} from '@angular/material';
import { CdkTableModule } from '@angular/cdk/table';
@NgModule({
imports: [
MatToolbarModule,
MatButtonModule,
MatIconModule,
MatCardModule,
MatGridListModule,
MatSnackBarModule,
MatSidenavModule,
MatListModule,
MatChipsModule,
MatFormFieldModule,
MatSelectModule,
MatOptionModule,
MatInputModule,
MatExpansionModule,
MatAutocompleteModule,
MatProgressSpinnerModule,
MatProgressBarModule,
MatTabsModule,
MatDialogModule,
MatMenuModule,
MatRadioModule,
MatStepperModule,
MatTooltipModule,
MatCheckboxModule,
MatDatepickerModule,
MatButtonToggleModule,
MatSliderModule,
MatSlideToggleModule,
MatTableModule,
MatPaginatorModule,
CdkTableModule,
MatSortModule,
],
exports: [
MatToolbarModule,
MatButtonModule,
MatIconModule,
MatCardModule,
MatGridListModule,
MatSnackBarModule,
MatSidenavModule,
MatListModule,
MatChipsModule,
MatFormFieldModule,
MatSelectModule,
MatOptionModule,
MatInputModule,
MatExpansionModule,
MatAutocompleteModule,
MatProgressSpinnerModule,
MatProgressBarModule,
MatTabsModule,
MatDialogModule,
MatMenuModule,
MatRadioModule,
MatStepperModule,
MatTooltipModule,
MatCheckboxModule,
MatDatepickerModule,
MatButtonToggleModule,
MatSliderModule,
MatSlideToggleModule,
MatTableModule,
MatPaginatorModule,
CdkTableModule,
MatSortModule,
]
})
export class MaterialModule { }

View File

@ -0,0 +1,66 @@
export class Guid {
private constructor(guid: string) {
if (!guid) { throw new TypeError('Invalid argument; `value` has no value.'); }
this.value = Guid.EMPTY;
if (guid && Guid.isGuid(guid)) {
this.value = guid;
}
}
public static validator = new RegExp('^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$', 'i');
public static EMPTY = '00000000-0000-0000-0000-000000000000';
private value: string;
public static isGuid(guid: any) {
const value: string = guid.toString();
return guid && (guid instanceof Guid || Guid.validator.test(value));
}
public static create(): Guid {
return new Guid([Guid.gen(2), Guid.gen(1), Guid.gen(1), Guid.gen(1), Guid.gen(3)].join('-'));
}
public static createEmpty(): Guid {
return new Guid('emptyguid');
}
public static parse(guid: string): Guid {
return new Guid(guid);
}
public static raw(): string {
return [Guid.gen(2), Guid.gen(1), Guid.gen(1), Guid.gen(1), Guid.gen(3)].join('-');
}
private static gen(count: number) {
let out = '';
for (let i = 0; i < count; i++) {
// tslint:disable-next-line:no-bitwise
out += (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}
return out;
}
public equals(other: Guid): boolean {
// Comparing string `value` against provided `guid` will auto-call
// toString on `guid` for comparison
return Guid.isGuid(other) && this.value === other.toString();
}
public isEmpty(): boolean {
return this.value === Guid.EMPTY;
}
public toString(): string {
return this.value;
}
public toJSON(): any {
return this.value;
}
}

View File

@ -0,0 +1,14 @@
import { Serializable } from './serializable';
export class JsonSerializer<T extends Serializable<T>> {
constructor(private constructorOfT: { new(): T }) {
}
public fromJSONArray(items: any[]): T[] {
return items.map(x => this.fromJSONObject(x));
}
public fromJSONObject(item: any): T {
return new this.constructorOfT().fromJSONObject(item);
}
}

View File

@ -0,0 +1,18 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { MaterialModule } from '../material/material.module';
@NgModule({
imports: [
CommonModule,
MaterialModule,
TranslateModule
],
exports: [
CommonModule,
MaterialModule,
TranslateModule
]
})
export class CommonUiModule { }

View File

@ -0,0 +1,27 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanLoad, Route, Router, RouterStateSnapshot } from '@angular/router';
import { AuthService } from './services/auth/auth.service';
@Injectable()
export class AuthGuard implements CanActivate, CanLoad {
constructor(private auth: AuthService, private router: Router) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
const url: string = state.url;
if (!this.auth.current()) {
this.router.navigate(['/unauthorized'], { queryParams: { returnUrl: url } });
return false;
}
return true;
}
canLoad(route: Route): boolean {
const url = `/${route.path}`;
if (!this.auth.current()) {
this.router.navigate(['/unauthorized'], { queryParams: { returnUrl: url } });
return false;
}
return true;
}
}

View File

@ -0,0 +1,5 @@
export enum AppRole {
Admin = 2,
Manager = 1,
User = 0,
}

View File

@ -0,0 +1,7 @@
export enum AuthProvider {
LinkedIn = 1,
Facebook = 2,
Twitter = 3,
Google = 4,
B2Access = 5
}

View File

@ -0,0 +1,4 @@
export enum DatasetProfileComboBoxType {
Autocomplete = "autocomplete",
WordList = "wordlist"
}

View File

@ -0,0 +1,8 @@
export enum DatasetProfileFieldViewStyle {
TextArea = "textarea",
BooleanDecision = "booleanDecision",
ComboBox = "combobox",
CheckBox = "checkBox",
FreeText = "freetext",
RadioBox = "radiobox"
}

View File

@ -0,0 +1,5 @@
export enum DatasetStatus {
Draft = 0,
Finalized = 1,
Deleted = 99
}

View File

@ -0,0 +1,5 @@
export enum DmpProfileFieldDataType {
Date = 0,
Number = 1,
Text = 2
}

View File

@ -0,0 +1,3 @@
export enum DmpProfileType {
Input = 0
}

View File

@ -0,0 +1,5 @@
export enum DmpStatus {
Draft = 0,
Finalized = 1,
Deleted = 99
}

View File

@ -0,0 +1,4 @@
export enum ExternalDatasetType {
Source = 0,
Output = 1
}

View File

@ -0,0 +1,4 @@
export enum ProjectType {
External = 0,
Internal = 1
}

View File

@ -0,0 +1,5 @@
export enum RecentActivityType {
Project = 0,
Dataset = 1,
Dmp = 2
}

View File

@ -0,0 +1,4 @@
export enum ValidationType {
None = 0,
Required = 1
}

View File

@ -0,0 +1,86 @@
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { CookieService } from 'ngx-cookie-service';
import { LanguageResolverService } from '../services/language-resolver/language-resolver.service';
import { LanguageService } from '../services/language/language.service';
import { AuthGuard } from './auth-guard.service';
import { AuthService } from './services/auth/auth.service';
import { CultureService } from './services/culture/culture-service';
import { DashboardService } from './services/dashboard/dashboard.service';
import { DatasetProfileService } from './services/dataset-profile/dataset-profile.service';
import { DatasetWizardService } from './services/dataset-wizard/dataset-wizard.service';
import { DatasetService } from './services/dataset/dataset.service';
import { DmpInvitationService } from './services/dmp/dmp-invitation.service';
import { DmpProfileService } from './services/dmp/dmp-profile.service';
import { DmpService } from './services/dmp/dmp.service';
import { ExternalDataRepositoryService } from './services/external-sources/data-repository/extternal-data-repository.service';
import { ExternalDatasetService } from './services/external-sources/dataset/external-dataset.service';
import { ExternalSourcesConfigurationService } from './services/external-sources/external-sources-configuration.service';
import { ExternalSourcesService } from './services/external-sources/external-sources.service';
import { ExternalRegistryService } from './services/external-sources/registry/external-registry.service';
import { ExternalResearcherService } from './services/external-sources/researcher/external-researcher.service';
import { ExternalServiceService } from './services/external-sources/service/external-service.service';
import { BaseHttpService } from './services/http/base-http.service';
import { LoggingService } from './services/logging/logging-service';
import { UiNotificationService } from './services/notification/ui-notification-service';
import { ProgressIndicationService } from './services/progress-indication/progress-indication-service';
import { ProjectFileUploadService } from './services/project/project-file-upload.service';
import { ProjectService } from './services/project/project.service';
import { SearchBarService } from './services/search-bar/search-bar.service';
import { TimezoneService } from './services/timezone/timezone-service';
import { UserService } from './services/user/user.service';
import { CollectionUtils } from './services/utilities/collection-utils.service';
import { TypeUtils } from './services/utilities/type-utils.service';
//
//
// This is shared module that provides all the services. Its imported only once on the AppModule.
//
//
@NgModule({
})
export class CoreServiceModule {
constructor(@Optional() @SkipSelf() parentModule: CoreServiceModule) {
if (parentModule) {
throw new Error(
'CoreModule is already loaded. Import it in the AppModule only');
}
}
static forRoot(): ModuleWithProviders {
return {
ngModule: CoreServiceModule,
providers: [
AuthService,
CookieService,
BaseHttpService,
AuthGuard,
LanguageService,
CultureService,
TimezoneService,
TypeUtils,
CollectionUtils,
UiNotificationService,
ProgressIndicationService,
LoggingService,
SearchBarService,
DashboardService,
LanguageResolverService,
ProjectService,
ProjectFileUploadService,
DmpService,
DmpProfileService,
ExternalSourcesService,
ExternalSourcesConfigurationService,
DatasetService,
DatasetWizardService,
ExternalDatasetService,
ExternalDataRepositoryService,
ExternalRegistryService,
ExternalResearcherService,
ExternalServiceService,
DatasetProfileService,
UserService,
DmpInvitationService
],
};
}
}

View File

@ -0,0 +1,25 @@
import { DatePipe } from '@angular/common';
import { NgModule } from '@angular/core';
import { NgForLimitPipe } from './pipes/ng-for-limit.pipe';
import { EnumUtils } from './services/utilities/enum-utils.service';
//
//
// This is shared module that provides all formatting utils. Its imported only once on the AppModule.
//
//
@NgModule({
declarations: [
NgForLimitPipe
],
exports: [
NgForLimitPipe
],
providers: [
EnumUtils,
DatePipe,
NgForLimitPipe
]
})
export class FormattingModule { }

View File

@ -0,0 +1,77 @@
import { ValidationType } from "../../../common/enum/validation-type";
export interface DatasetProfile {
label: string;
sections: Section[];
pages: Page[];
}
export interface Page {
id: string;
ordinal: number;
title: string;
}
export interface Section {
sections: Section[];
fieldSets: FieldSet[];
defaultVisibility: boolean;
page: string;
ordinal: number;
id: string;
title: string;
description: string;
}
export interface FieldSet {
id: string;
ordinal: number;
multiplicity: Multiplicity;
title: string;
description: string;
extendedDescription: string;
hasCommentField: boolean;
fields: Field[];
}
export interface Multiplicity {
min: number;
max: number;
}
export interface Field {
id: string;
ordinal: number;
value: string;
viewStyle: ViewStyle;
datatype: string;
page: number;
defaultValue: DefaultValue;
data: any;
visible: Visibility;
validations: ValidationType[];
}
export interface ViewStyle {
renderStyle: string;
cssClass: string;
}
export interface DefaultValue {
type: string;
value: string;
}
export interface Visibility {
rules: Rule[];
style: string;
}
export interface Rule {
ruleType: string;
target: string;
ruleStyle: string;
value: string;
valueType: string;
}

View File

@ -0,0 +1,4 @@
export interface Credential {
username: string;
secret: string;
}

View File

@ -0,0 +1,7 @@
import { AuthProvider } from "../../common/enum/auth-provider";
export interface LoginInfo {
ticket: string;
provider: AuthProvider;
data?: any;
}

View File

@ -0,0 +1,13 @@
import { AppRole } from "../../common/enum/app-role";
export interface Principal {
id: number;
token: string;
name: string;
expiresAt: Date;
authorities: AppRole[];
avatarUrl: string;
timezone: string;
language: string;
culture: string;
}

View File

@ -0,0 +1,4 @@
import { FormBuilder } from '@angular/forms';
export abstract class BaseFormModel {
public formBuilder: FormBuilder = new FormBuilder();
}

View File

@ -0,0 +1,5 @@
export interface CultureInfo {
name: string;
displayName: string;
nativeName: string;
}

View File

@ -0,0 +1,7 @@
import { Serializable } from "../../../common/types/json/serializable";
export interface DashboardStatisticsModel {
totalDataManagementPlanCount: number;
totalProjectCount: number;
totalDataSetCount: number;
}

View File

@ -0,0 +1,7 @@
import { SearchBarType } from "../../../ui/misc/navigation/navigation.component";
export interface SearchBarItem {
id: string;
label: string;
type: SearchBarType;
}

View File

@ -0,0 +1,8 @@
export interface DataRepositoryModel {
id: string;
label: string;
abbreviation: string;
uri: string;
pid: string;
info: string;
}

View File

@ -0,0 +1,3 @@
export class ColumnOrdering {
public fields: Array<string> = new Array();
}

View File

@ -0,0 +1,14 @@
import { RequestItem } from '../../query/request-item';
import { ColumnOrdering } from './column-ordering';
export class DataTableRequest<T> extends RequestItem<T> {
offset = 0;
length = 0;
public orderings: ColumnOrdering;
constructor(offset: number, length: number, orderings: ColumnOrdering) {
super();
this.length = length;
this.offset = offset;
this.orderings = orderings;
}
}

View File

@ -0,0 +1,16 @@
import { Field } from './field';
import { Multiplicity } from './multiplicity';
export interface CompositeField {
fields: Array<Field>;
ordinal: number;
id: string;
numbering: string;
multiplicity: Multiplicity;
multiplicityItems: Array<CompositeField>;
title: string;
description: string;
extendedDescription: string;
hasCommentField: boolean;
commentFieldValue: string;
}

View File

@ -0,0 +1,8 @@
import { Page } from "./page";
import { Rule } from "./rule";
export interface DatasetProfileDefinitionModel {
status: number;
pages: Page[];
rules: Rule[];
}

View File

@ -0,0 +1,4 @@
export interface DefaultValue {
type: string;
value: string;
}

View File

@ -0,0 +1,42 @@
import { DatasetProfileComboBoxType } from "../../../common/enum/dataset-profile-combo-box-type";
export interface FieldData {
label: string;
}
export interface AutoCompleteFieldData extends FieldData {
type: string;
url: string;
optionsRoot: string;
autoCompleteOptions: FieldDataOption;
}
export interface CheckBoxFieldData extends FieldData {
}
export interface BooleanDecisionFieldData extends FieldData {
}
export interface FreeTextFieldData extends FieldData {
}
export interface RadioBoxFieldData extends FieldData {
options: Array<FieldDataOption>;
}
export interface TextAreaFieldData extends FieldData {
}
export interface WordListFieldData extends FieldData {
type: DatasetProfileComboBoxType;
options: Array<FieldDataOption>;
}
export interface FieldDataOption extends FieldData {
label: string;
value: string;
}

View File

@ -0,0 +1,51 @@
// import { FormGroup } from "@angular/forms";
// import { JsonSerializer } from "../../../common/types/json/json-serializer";
// import { Serializable } from "../../../common/types/json/serializable";
// import { BaseModel } from "../../../models/BaseModel";
// import { CompositeField } from "./composite-field";
// export class FieldGroup extends BaseModel implements Serializable<FieldGroup> {
// public id: string;
// public title: string;
// public section: string;
// public value: string;
// public description: string;
// public extendedDescription: string;
// public defaultVisibility: boolean;
// public page: number;
// public compositeFields: Array<CompositeField> = new Array<CompositeField>();
// fromJSONObject(item: any): FieldGroup {
// this.id = item.id;
// this.title = item.title;
// this.value = item.value;
// this.description = item.description;
// this.extendedDescription = item.extendedDescription;
// this.defaultVisibility = item.defaultVisibility;
// this.page = item.page;
// this.compositeFields = new JsonSerializer<CompositeField>(CompositeField).fromJSONArray(item.compositeFields);
// return this;
// }
// buildForm(): FormGroup {
// const formGroup: FormGroup = this.formBuilder.group({
// /* id: [this.id],
// title: [this.title],
// value: [this.value],
// description: [this.description],
// extendedDescription: [this.extendedDescription],
// defaultVisibility: [this.defaultVisibility],
// page: [this.page] */
// });
// const compositeFieldsFormArray = new Array<FormGroup>();
// if (this.compositeFields) {
// this.compositeFields.forEach(item => {
// const form: FormGroup = item.buildForm();
// compositeFieldsFormArray.push(form);
// });
// }
// formGroup.addControl('compositeFields', this.formBuilder.array(compositeFieldsFormArray));
// return formGroup;
// }
// }

View File

@ -0,0 +1,23 @@
import { ValidationType } from "../../common/enum/validation-type";
import { DefaultValue } from "./default-value";
import { Multiplicity } from "./multiplicity";
import { ViewStyle } from "./view-style";
export interface Field {
id: string;
title: string;
value: any;
defaultValue: DefaultValue;
description: string;
numbering: string;
extendedDescription: string;
viewStyle: ViewStyle;
defaultVisibility: boolean;
page: number;
multiplicity: Multiplicity;
multiplicityItems: Array<Field>;
data: any;
validations: Array<ValidationType>;
validationRequired;
}

View File

@ -0,0 +1,7 @@
export interface Multiplicity {
min: number;
max: number;
}

View File

@ -0,0 +1,7 @@
import { Section } from "./section";
export interface Page {
ordinal: number;
title: string;
sections: Array<Section>;
}

View File

@ -0,0 +1,6 @@
export class Rule {
sourceField: string;
targetField: string;
requiredValue: any;
type: string;
}

View File

@ -0,0 +1,13 @@
import { CompositeField } from "./composite-field";
export interface Section {
sections: Array<Section>;
defaultVisibility: boolean;
page: number;
numbering: string;
ordinal: number;
id: string;
title: string;
description: string;
compositeFields: Array<CompositeField>;
}

View File

@ -0,0 +1,4 @@
export interface ViewStyle {
cssClass: string;
renderStyle: string;
}

View File

@ -0,0 +1,13 @@
export interface DatasetListingModel {
id: String;
label: String;
dmp: String;
project: String;
profile: String;
dataRepositories: String;
registries: String;
services: String;
description: String;
status: Number;
created: Date;
}

View File

@ -0,0 +1,33 @@
export interface DatasetProfileModel {
id: String;
label: String;
}
// export class DatasetProfileModel implements Serializable<DatasetProfileModel> {
// public id: String;
// public label: String;
// fromJSONObject(item: any): DatasetProfileModel {
// this.id = item.id;
// this.label = item.label;
// return this;
// }
// buildForm(context: ValidationContext = null, disabled: boolean = false): FormGroup {
// if (context == null) { context = this.createValidationContext(); }
// const formGroup = new FormBuilder().group({
// id: [{ value: this.id, disabled: disabled }],
// });
// return formGroup;
// }
// createValidationContext(): ValidationContext {
// const baseContext: ValidationContext = new ValidationContext();
// //baseContext.validation.push({ key: 'id', validators: [Validators.required, BackendErrorValidator(this.errorModel, 'id')] });
// return baseContext;
// }
// }

View File

@ -0,0 +1,24 @@
import { DataRepositoryModel } from "../data-repository/data-repository";
import { DatasetProfileDefinitionModel } from "../dataset-profile-definition/dataset-profile-definition";
import { DmpModel } from "../dmp/dmp";
import { ExternalDatasetModel } from "../external-dataset/external-dataset";
import { RegistryModel } from "../registry/registry";
import { ServiceModel } from "../service/service";
import { TagModel } from "../tag/tag";
import { DatasetProfileModel } from "./dataset-profile";
export interface DatasetWizardModel {
id?: string;
label?: string;
uri?: String;
description?: String;
status?: number;
dmp?: DmpModel;
datasetProfileDefinition?: DatasetProfileDefinitionModel;
registries?: RegistryModel[];
services?: ServiceModel[];
dataRepositories?: DataRepositoryModel[];
tags?: TagModel[];
externalDatasets?: ExternalDatasetModel[];
profile?: DatasetProfileModel;
}

View File

@ -0,0 +1,73 @@
import { DataRepositoryModel } from '../data-repository/data-repository';
import { RegistryModel } from '../registry/registry';
import { ServiceModel } from '../service/service';
export interface DatasetModel {
id: String;
label: String;
profile: String;
uri: String;
status: String;
description: String;
services: ServiceModel[];
registries: RegistryModel[];
dataRepositories: DataRepositoryModel[];
}
// export class DatasetModel implements Serializable<DatasetModel> {
// public id: String;
// public label: String;
// public profile: String;
// public uri: String;
// public status: String;
// public description: String;
// public services: ServiceModel[] = [];
// public registries: RegistryModel[] = [];
// public dataRepositories: DataRepositoryModel[] = [];
// public errorModel: ValidationErrorModel = new ValidationErrorModel();
// fromJSONObject(item: any): DatasetModel {
// this.id = item.id;
// this.label = item.label;
// this.profile = item.profile;
// this.uri = item.uri;
// this.status = item.status;
// this.description = item.description;
// this.services = new JsonSerializer<ServiceModel>(ServiceModel).fromJSONArray(item.services);
// this.registries = new JsonSerializer<RegistryModel>(RegistryModel).fromJSONArray(item.registries);
// this.dataRepositories = new JsonSerializer<DataRepositoryModel>(DataRepositoryModel).fromJSONArray(item.dataRepositories);
// return this;
// }
// buildForm(context: ValidationContext = null, disabled: boolean = false): FormGroup {
// if (context == null) { context = this.createValidationContext(); }
// const formGroup = new FormBuilder().group({
// label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators],
// profile: [{ value: this.profile, disabled: disabled }, context.getValidation('profile').validators],
// uri: [{ value: this.uri, disabled: disabled }, context.getValidation('uri').validators],
// status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators],
// description: [{ value: this.description, disabled: disabled }, context.getValidation('description').validators],
// services: [{ value: this.services, disabled: disabled }, context.getValidation('services').validators],
// registries: [{ value: this.registries, disabled: disabled }, context.getValidation('registries').validators],
// dataRepositories: [{ value: this.dataRepositories, disabled: disabled }, context.getValidation('dataRepositories').validators]
// });
// return formGroup;
// }
// createValidationContext(): ValidationContext {
// const baseContext: ValidationContext = new ValidationContext();
// baseContext.validation.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.errorModel, 'label')] });
// baseContext.validation.push({ key: 'profile', validators: [Validators.required, BackendErrorValidator(this.errorModel, 'profile')] });
// baseContext.validation.push({ key: 'uri', validators: [BackendErrorValidator(this.errorModel, 'uri')] });
// baseContext.validation.push({ key: 'status', validators: [Validators.required, BackendErrorValidator(this.errorModel, 'status')] });
// baseContext.validation.push({ key: 'description', validators: [BackendErrorValidator(this.errorModel, 'description')] });
// baseContext.validation.push({ key: 'services', validators: [Validators.required, BackendErrorValidator(this.errorModel, 'services')] });
// baseContext.validation.push({ key: 'registries', validators: [Validators.required, BackendErrorValidator(this.errorModel, 'registries')] });
// baseContext.validation.push({ key: 'dataRepositories', validators: [Validators.required, BackendErrorValidator(this.errorModel, 'dataRepositories')] });
// return baseContext;
// }
// }

View File

@ -0,0 +1,11 @@
import { DmpProfileFieldDataType } from '../../common/enum/dmp-profile-field-type';
import { DmpProfileType } from '../../common/enum/dmp-profile-type';
export interface DmpProfileField {
id: string;
type: DmpProfileType;
dataType: DmpProfileFieldDataType;
required: boolean;
label: string;
value: any;
}

View File

@ -0,0 +1,10 @@
import { DmpProfileDefinition } from "./dmp-profile";
export interface DmpProfileListing {
id: string;
label: string;
definition: DmpProfileDefinition;
status: number;
created: Date;
modified: Date;
}

View File

@ -0,0 +1,15 @@
import { DmpProfileField } from "./dmp-profile-field";
export interface DmpProfile {
id: string;
label: string;
definition: DmpProfileDefinition;
status: number;
created: Date;
modified: Date;
}
export interface DmpProfileDefinition {
fields: DmpProfileField[];
}

View File

@ -0,0 +1,4 @@
export interface DmpDynamicFieldDependency {
id: string;
queryProperty: string;
}

View File

@ -0,0 +1,10 @@
import { DmpDynamicFieldDependency } from "./dmp-dynamic-field-dependency";
export interface DmpDynamicField {
id: string;
name: string;
required: boolean;
queryProperty;
value: string;
dependencies: Array<DmpDynamicFieldDependency>;
}

View File

@ -0,0 +1,14 @@
import { DmpStatus } from "../../common/enum/dmp-status";
export interface DmpListingModel {
id: String;
label: String;
status: DmpStatus;
project: String;
profile: String;
creationTime: String;
organisations: String;
groupId: string;
version: number;
datasets: any[];
}

View File

@ -0,0 +1,26 @@
import { Status } from "../../common/enum/Status";
import { DmpProfile, DmpProfileDefinition } from "../dmp-profile/dmp-profile";
import { OrganizationModel } from "../organisation/organization";
import { ProjectModel } from "../project/project";
import { ResearcherModel } from "../researcher/researcher";
import { UserModel } from "../user/user";
import { DmpDynamicField } from "./dmp-dynamic-field";
export interface DmpModel {
id: string;
label: string;
groupId: String;
profile: String;
version: number;
status: Status;
lockable: boolean;
description: String;
profiles: DmpProfile[];
project: ProjectModel;
organisations: OrganizationModel[];
researchers: ResearcherModel[];
associatedUsers: UserModel[];
creator: UserModel;
definition: DmpProfileDefinition;
dynamicFields: Array<DmpDynamicField>;
}

View File

@ -0,0 +1,25 @@
import { FormBuilder, FormGroup } from '@angular/forms';
import { Serializable } from '../../../../common/types/json/serializable';
export class DmpInvitationUser implements Serializable<DmpInvitationUser> {
public id: string;
public email: string;
public name: string;
fromJSONObject(item: any): DmpInvitationUser {
this.id = item.id;
this.email = item.email;
this.name = item.name;
return this;
}
buildForm(): FormGroup {
return new FormBuilder().group({
id: [this.id],
email: [this.email],
name: [this.name]
});
}
}

View File

@ -0,0 +1,17 @@
import { FormBuilder, FormGroup } from '@angular/forms';
import { DmpInvitationUser } from './dmp-invitation-user';
export class DmpInvitation {
public dataManagementPlan: string;
public users = new Array<DmpInvitationUser>();
buildForm(): FormGroup {
const formGroup = new FormBuilder().group({
dataManagementPlan: [this.dataManagementPlan],
users: [this.users]
});
return formGroup;
}
}

View File

@ -0,0 +1,10 @@
import { ExternalDatasetType } from '../../common/enum/external-dataset-type';
export interface ExternalDatasetModel {
abbreviation: String;
id: String;
label: String;
reference: String;
type: ExternalDatasetType;
info: String;
}

View File

@ -0,0 +1,5 @@
export interface ExternalSourceItemModel {
id: String;
name: String;
description: String;
}

View File

@ -0,0 +1,4 @@
export class ExternalSourceUrlModel {
public key: String;
public label: String;
}

View File

@ -0,0 +1,9 @@
import { ExternalSourceUrlModel } from "./external-source-url";
export class ExternalSourcesConfiguration {
public registries: Array<ExternalSourceUrlModel>;
public dataRepositories: Array<ExternalSourceUrlModel>;
public services: Array<ExternalSourceUrlModel>;
public externalDatasets: Array<ExternalSourceUrlModel>;
public tags: Array<ExternalSourceUrlModel>;
}

Some files were not shown because too many files have changed in this diff Show More